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 } |