Mercurial > noffle
comparison src/request.c @ 43:2842f50feb55 noffle
[svn] * client.c, client.h, common.h, config.c, config.h, content.c, content.h,
control.c, control.h, database.c, database.h, dynamicstring.c,
dynamicstring.h, fetch.c, fetch.h, fetchlist.c, fetchlist.h, group.c,
group.h, itemlist.c, itemlist.h, lock.c, lock.h, log.c, log.h, noffle.c,
online.c, online.h, outgoing.c, outgoing.h, over.c, over.h, post.c, post.h,
protocol.c, protocol.h, pseudo.c, pseudo.h, request.c, request.h, server.c,
server.h, util.c, util.h, wildmat.c, wildmat.h: Moved files to the
subdirectory src/
* Makefile.am, acconfig.h, configure.in, docs/Makefile.am, src/Makefile.am,
Makefile.in, aclocal.m4, config.h.in, configure, install-sh, missing,
mkinstalldirs, stamp-h.in, docs/Makefile.in, src/Makefile.in: Added files.
They are used by aclocal, autoheader, autoconf and automake.
* src/config.c, src/config.h: Renamed to configfile.c and configfile.h,
because configure will generate a config.h file itself.
* src/client.c, src/content.c, src/database.c, src/fetch.c, src/fetchlist.c,
src/group.c, src/lock.c, src/noffle.c, src/online.c, src/outgoing.c,
src/over.c, src/pseudo.c, src/request.c, src/server.c, src/util.c:
Changed '#include "config.h"' to '#include "configfile.h"'.
* src/client.c, src/content.c, src/database.c, src/fetch.c, src/fetchlist.c,
src/group.c, src/lock.c, src/online.c, src/outgoing.c, src/post.c,
src/protocol.c, src/request.c, src/server.c: Files now #include <config.h>.
Added missing <stdio.h>. This removes the warnings about snprintf() not
being declared.
* Makefile: Removed. This is now generated by configure.
| author | uh1763 |
|---|---|
| date | Fri, 05 May 2000 22:45:56 +0100 |
| parents | |
| children | 32ba1198c6fa |
comparison
equal
deleted
inserted
replaced
| 42:2467ff423c15 | 43:2842f50feb55 |
|---|---|
| 1 /* | |
| 2 request.c | |
| 3 | |
| 4 Collection of articles that are marked for download. | |
| 5 | |
| 6 $Id: request.c 49 2000-05-05 21:45:56Z uh1763 $ | |
| 7 */ | |
| 8 | |
| 9 #if HAVE_CONFIG_H | |
| 10 #include <config.h> | |
| 11 #endif | |
| 12 | |
| 13 #include <stdio.h> | |
| 14 #include "request.h" | |
| 15 #include <dirent.h> | |
| 16 #include <errno.h> | |
| 17 #include <fcntl.h> | |
| 18 #include <sys/types.h> | |
| 19 #include <sys/stat.h> | |
| 20 #include <unistd.h> | |
| 21 #include <assert.h> | |
| 22 #include "configfile.h" | |
| 23 #include "log.h" | |
| 24 #include "util.h" | |
| 25 | |
| 26 | |
| 27 /* This struct keeps record of the message IDs that are to be fetched from | |
| 28 one particular host. Several of these are chained together via the | |
| 29 "next" pointer, if we have several servers. | |
| 30 */ | |
| 31 | |
| 32 struct Reqserv; | |
| 33 typedef struct Reqserv Reqserv; | |
| 34 | |
| 35 struct Reqserv { | |
| 36 char* serv; /* Server the messages are to be requested | |
| 37 from */ | |
| 38 char** reql; /* List of message IDs of requested | |
| 39 messages. Some entries (that have been | |
| 40 deleted) may be NULL */ | |
| 41 int reql_length; /* Number of string pointers in reql, | |
| 42 including NULL entries */ | |
| 43 int reql_capacity; /* maximum number of string pointers reql | |
| 44 can hold */ | |
| 45 Bool dirty; /* whether the request list needs to be | |
| 46 rewritten to disk */ | |
| 47 Reqserv* next; /* next Reqserv in list */ | |
| 48 time_t mtime; /* last modification time of request file */ | |
| 49 }; | |
| 50 | |
| 51 /* List of servers */ | |
| 52 static Reqserv* reqserv = 0; | |
| 53 | |
| 54 /* sanity check */ | |
| 55 static Bool is_open = FALSE; | |
| 56 | |
| 57 /* for Req_first/Req_next */ | |
| 58 static char** iterator = 0; | |
| 59 static char** iterator_end = 0; | |
| 60 | |
| 61 | |
| 62 /* local functions */ | |
| 63 static Reqserv* newReqserv (const char* serv); | |
| 64 static Bool getReqserv (const char* serv, Reqserv** rsz); | |
| 65 static void fileRequest (Str file, const char *serv); | |
| 66 static char** searchMsgId (const Reqserv * rs, const char *msgId); | |
| 67 static void storeMsgId (Reqserv* rs, const char* msgId); | |
| 68 static Bool readRequestfile (const char* serv, Reqserv** rsz); | |
| 69 static time_t get_mtime (const char* serv); | |
| 70 | |
| 71 /* read modification time of request file */ | |
| 72 static time_t get_mtime(const char* serv) | |
| 73 { | |
| 74 Str filename; | |
| 75 struct stat stat1; | |
| 76 | |
| 77 fileRequest(filename, serv); | |
| 78 stat(filename, &stat1); | |
| 79 return stat1.st_mtime; | |
| 80 } | |
| 81 | |
| 82 | |
| 83 /* create new Reqserv and queue it */ | |
| 84 static Reqserv* newReqserv(const char* serv) | |
| 85 { | |
| 86 Reqserv* rs = (Reqserv*) malloc(sizeof(Reqserv)); | |
| 87 rs->serv = strcpy(malloc(strlen(serv)+1), serv); | |
| 88 rs->reql = 0; | |
| 89 rs->reql_length = 0; | |
| 90 rs->reql_capacity = 0; | |
| 91 rs->next = reqserv; | |
| 92 rs->dirty = FALSE; | |
| 93 rs->mtime = 0; | |
| 94 reqserv = rs; | |
| 95 return rs; | |
| 96 } | |
| 97 | |
| 98 | |
| 99 /* get Reqserv for given server, and save it in "rsz". Load from file as | |
| 100 necessary. Return TRUE on success. Otherwise log errors and return | |
| 101 FALSE. (details in errno) | |
| 102 */ | |
| 103 static Bool getReqserv(const char* serv, Reqserv** rsz) | |
| 104 { | |
| 105 Reqserv* rs; | |
| 106 for (rs = reqserv; rs; rs = rs->next) | |
| 107 if (!strcmp(serv, rs->serv)) { | |
| 108 *rsz = rs; | |
| 109 return TRUE; | |
| 110 } | |
| 111 return readRequestfile(serv, rsz); | |
| 112 } | |
| 113 | |
| 114 | |
| 115 /* Delete Reqserv from cache, if not up-to-date */ | |
| 116 static void | |
| 117 cleanupReqserv( void ) | |
| 118 { | |
| 119 Reqserv *rs, *prev, *next; | |
| 120 | |
| 121 rs = reqserv; | |
| 122 prev = NULL; | |
| 123 while ( rs != NULL ) | |
| 124 { | |
| 125 ASSERT( ! rs->dirty ); | |
| 126 next = rs->next; | |
| 127 if ( get_mtime( rs->serv ) != rs->mtime ) | |
| 128 { | |
| 129 if ( prev != NULL ) | |
| 130 prev->next = next; | |
| 131 else | |
| 132 reqserv = next; | |
| 133 free( rs->serv ); | |
| 134 rs->serv = NULL; | |
| 135 free( rs->reql ); | |
| 136 rs->reql = NULL; | |
| 137 free( rs ); | |
| 138 } | |
| 139 prev = rs; | |
| 140 rs = next; | |
| 141 } | |
| 142 } | |
| 143 | |
| 144 /* Save name of file storing requests from server "serv" in "file" */ | |
| 145 static void fileRequest( Str file, const char *serv) | |
| 146 { | |
| 147 snprintf( file, MAXCHAR, "%s/requested/%s", Cfg_spoolDir(), serv); | |
| 148 } | |
| 149 | |
| 150 | |
| 151 /* Search for msgid in Reqserv. Return pointer to list entry. Return 0 if | |
| 152 list does not contain msgid. */ | |
| 153 static char** searchMsgId(const Reqserv * rs, const char *msgId ) | |
| 154 { | |
| 155 char** rz; | |
| 156 ASSERT(rs != 0); | |
| 157 | |
| 158 if (!rs->reql) | |
| 159 return 0; | |
| 160 | |
| 161 for (rz = rs->reql; rz < rs->reql + rs->reql_length; rz++) | |
| 162 if (*rz && !strcmp(*rz, msgId)) | |
| 163 return rz; | |
| 164 | |
| 165 return 0; | |
| 166 } | |
| 167 | |
| 168 | |
| 169 Bool | |
| 170 Req_contains(const char *serv, const char *msgId) | |
| 171 { | |
| 172 Reqserv* rs; | |
| 173 ASSERT( is_open ); | |
| 174 if (getReqserv(serv, &rs) == FALSE) | |
| 175 return FALSE; | |
| 176 return searchMsgId(rs, msgId) ? TRUE : FALSE; | |
| 177 } | |
| 178 | |
| 179 | |
| 180 static void storeMsgId(Reqserv* rs, const char* msgId) | |
| 181 { | |
| 182 char* msgid; | |
| 183 | |
| 184 if (searchMsgId(rs, msgId)) | |
| 185 /* already recorded */ | |
| 186 return; | |
| 187 | |
| 188 msgid = strcpy(malloc(strlen(msgId)+1), msgId); | |
| 189 | |
| 190 if (rs->reql_length >= rs->reql_capacity) { | |
| 191 int c1 = rs->reql_capacity*2 + 10; | |
| 192 rs->reql = (char**) realloc(rs->reql, c1*sizeof(char*)); | |
| 193 rs->reql_capacity = c1; | |
| 194 } | |
| 195 | |
| 196 *(rs->reql + rs->reql_length++) = msgid; | |
| 197 rs->dirty = TRUE; | |
| 198 } | |
| 199 | |
| 200 | |
| 201 /* Add request for message "msgIg" from server "serv". Return TRUE iff | |
| 202 successful. | |
| 203 */ | |
| 204 Bool Req_add(const char *serv, const char *msgId) | |
| 205 { | |
| 206 Reqserv* rs; | |
| 207 ASSERT( is_open ); | |
| 208 Log_dbg( "Marking %s on %s for download", msgId, serv ); | |
| 209 | |
| 210 if (getReqserv(serv, &rs) == FALSE) | |
| 211 return FALSE; | |
| 212 storeMsgId(rs, msgId); | |
| 213 return TRUE; | |
| 214 } | |
| 215 | |
| 216 static Bool | |
| 217 readLn( Str line, FILE* f ) | |
| 218 { | |
| 219 size_t len; | |
| 220 | |
| 221 if ( ! fgets( line, MAXCHAR, f ) ) | |
| 222 return FALSE; | |
| 223 len = strlen( line ); | |
| 224 if ( line[ len - 1 ] == '\n' ) | |
| 225 line[ len - 1 ] = '\0'; | |
| 226 return TRUE; | |
| 227 } | |
| 228 | |
| 229 /* Read request file into new, non-queued Reqserv. Save new Reqserv in | |
| 230 "rsz" and return TRUE on success. Returns FALSE on failure, see errno. | |
| 231 If the file doesn't exist, an empty Reqserv is returned. | |
| 232 */ | |
| 233 static Bool readRequestfile(const char* serv, Reqserv** rsz) | |
| 234 { | |
| 235 Str filename; | |
| 236 Str line; | |
| 237 FILE* file; | |
| 238 Reqserv* rs; | |
| 239 | |
| 240 fileRequest(filename, serv); | |
| 241 Log_dbg("reading request file %s", filename); | |
| 242 | |
| 243 file = fopen(filename, "r"); | |
| 244 if (!file && (errno == ENOENT)) { | |
| 245 *rsz = newReqserv(serv); | |
| 246 (*rsz)->mtime = get_mtime(serv); | |
| 247 return TRUE; | |
| 248 } | |
| 249 if (Log_check(file != 0, | |
| 250 "could not open %s for reading: %s", | |
| 251 filename, strerror(errno))) | |
| 252 return FALSE; | |
| 253 | |
| 254 rs = *rsz = newReqserv(serv); | |
| 255 | |
| 256 while( readLn(line, file) == TRUE) { | |
| 257 char* line1 = Utl_stripWhiteSpace(line); | |
| 258 if (*line1) | |
| 259 storeMsgId(rs, line1); | |
| 260 } | |
| 261 | |
| 262 rs->dirty = FALSE; | |
| 263 | |
| 264 if (Log_check(fclose(file) != EOF, | |
| 265 "could not close %s properly: %s\n", | |
| 266 filename, strerror(errno))) | |
| 267 return FALSE; | |
| 268 | |
| 269 return TRUE; | |
| 270 } | |
| 271 | |
| 272 | |
| 273 /* Write out request file for given Reqserv. Return TRUE on success. If an | |
| 274 I/O error occurs, it is logged, and FALSE is returned. | |
| 275 */ | |
| 276 static Bool writeRequestfile(Reqserv* rs) | |
| 277 { | |
| 278 Str filename; | |
| 279 FILE* file; | |
| 280 char** z; | |
| 281 | |
| 282 fileRequest(filename, rs->serv); | |
| 283 Log_dbg("writing request file %s", filename); | |
| 284 | |
| 285 if (Log_check((file = fopen(filename, "w")) != 0, | |
| 286 "could not open %s for writing: %s", | |
| 287 filename, strerror(errno))) | |
| 288 return FALSE; | |
| 289 | |
| 290 if (rs->reql) | |
| 291 for (z = rs->reql; z < rs->reql+rs->reql_length; z++) | |
| 292 if (*z) { | |
| 293 if (Log_check( fputs(*z, file) != EOF | |
| 294 && fputs("\n", file) != EOF, | |
| 295 "write error: %s", strerror(errno))) | |
| 296 return FALSE; | |
| 297 } | |
| 298 | |
| 299 if (Log_check(fclose(file) != EOF, | |
| 300 "could not close %s properly: %s\n", | |
| 301 filename, strerror(errno))) | |
| 302 return FALSE; | |
| 303 | |
| 304 rs->dirty = FALSE; | |
| 305 rs->mtime = get_mtime(rs->serv); | |
| 306 | |
| 307 return TRUE; | |
| 308 } | |
| 309 | |
| 310 | |
| 311 void | |
| 312 Req_remove( const char *serv, const char *msgId ) | |
| 313 { | |
| 314 Reqserv* rs; | |
| 315 char** z; | |
| 316 | |
| 317 ASSERT( is_open ); | |
| 318 Log_dbg("Req_remove(\"%s\", \"%s\")", serv, msgId); | |
| 319 | |
| 320 if (getReqserv(serv, &rs) == FALSE) | |
| 321 return; | |
| 322 | |
| 323 z = searchMsgId(rs, msgId); | |
| 324 if ( ! z ) | |
| 325 return; | |
| 326 | |
| 327 free(*z); | |
| 328 *z = 0; | |
| 329 rs->dirty = TRUE; | |
| 330 } | |
| 331 | |
| 332 | |
| 333 Bool | |
| 334 Req_first( const char *serv, Str msgId ) | |
| 335 { | |
| 336 Reqserv* rs; | |
| 337 | |
| 338 ASSERT( is_open ); | |
| 339 ASSERT( !iterator && !iterator_end ); | |
| 340 | |
| 341 if (getReqserv(serv, &rs) == FALSE) | |
| 342 return FALSE; | |
| 343 | |
| 344 if (!rs->reql) | |
| 345 return FALSE; | |
| 346 | |
| 347 iterator = rs->reql - 1; | |
| 348 iterator_end = rs->reql + rs->reql_length; | |
| 349 | |
| 350 return Req_next(msgId); | |
| 351 } | |
| 352 | |
| 353 | |
| 354 Bool | |
| 355 Req_next( Str msgId ) | |
| 356 { | |
| 357 ASSERT( is_open ); | |
| 358 ASSERT(iterator && iterator_end); | |
| 359 | |
| 360 if (iterator >= iterator_end) | |
| 361 return FALSE; | |
| 362 iterator++; | |
| 363 | |
| 364 while (iterator < iterator_end) { | |
| 365 if (!*iterator) | |
| 366 iterator++; | |
| 367 else { | |
| 368 Utl_cpyStr(msgId, *iterator); | |
| 369 return TRUE; | |
| 370 } | |
| 371 } | |
| 372 | |
| 373 iterator = iterator_end = 0; | |
| 374 return FALSE; | |
| 375 } | |
| 376 | |
| 377 | |
| 378 /* Get exclusive access to all request files. Maybe we already have had it, | |
| 379 and the cache is outdated. So we delete request files, which have | |
| 380 changed recently, from cache. These files will be reread on demand. | |
| 381 */ | |
| 382 Bool | |
| 383 Req_open(void) | |
| 384 { | |
| 385 Log_dbg("opening request database"); | |
| 386 ASSERT(is_open == FALSE); | |
| 387 cleanupReqserv(); | |
| 388 is_open = TRUE; | |
| 389 return TRUE; | |
| 390 } | |
| 391 | |
| 392 | |
| 393 /* Do not occupy the request files any longer. Write any changes to disk. | |
| 394 Return TRUE on success, FALSE if an IO error occurs. */ | |
| 395 void Req_close(void) | |
| 396 { | |
| 397 Bool ret = TRUE; | |
| 398 Reqserv* rs; | |
| 399 Log_dbg("closing request database, writing changes to disk"); | |
| 400 ASSERT(is_open == TRUE); | |
| 401 | |
| 402 for (rs = reqserv; rs; rs = rs->next) { | |
| 403 if (rs->dirty == TRUE) { | |
| 404 if (!writeRequestfile(rs)) | |
| 405 ret = FALSE; | |
| 406 } | |
| 407 } | |
| 408 | |
| 409 is_open = FALSE; | |
| 410 } |
