chiark / gitweb /
Better message for 503 from peer on idle conn. Conventional macro protection for...
[inn-innduct.git] / storage / ov.c
1 /*  $Id: ov.c 6135 2003-01-19 01:15:40Z rra $
2 **
3 **  The implementation of the overview API.
4 **
5 **  This code handles calls to the overview API by passing them along to the
6 **  appropriate underlying overview method, as well as implementing those
7 **  portions of the overview subsystem that are independent of storage
8 **  method.
9 */
10
11 #include "config.h"
12 #include "clibrary.h"
13 #include <assert.h>
14 #include <ctype.h>
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <sys/stat.h>
18 #include <syslog.h>
19
20 #include "inn/innconf.h"
21 #include "libinn.h"
22 #include "ov.h"
23 #include "ovinterface.h"
24 #include "ovmethods.h"
25
26 /* FIXME: The following variables are shared between this file and expire.c.
27    This should be cleaned up with a better internal interface. */
28 static bool     OVdelayrm;
29 static OV_METHOD        ov;
30
31 bool
32 OVopen(int mode)
33 {
34     int i;
35     bool val;
36     char *p;
37
38     if (ov.open)
39         /* already opened */
40         return true;
41
42     /* if innconf isn't already read in, do so. */
43     if (innconf == NULL)
44         if (!innconf_read(NULL))
45             return false;
46     if (!innconf->enableoverview) {
47         syslog(L_FATAL, "enableoverview is not true");
48         fprintf(stderr, "enableoverview is not true\n");
49         return false;
50     }
51     if (innconf->ovmethod == NULL) {
52         syslog(L_FATAL, "ovmethod is not defined");
53         fprintf(stderr, "ovmethod is not defined\n");
54         return false;
55     }
56     for (i=0;i<NUM_OV_METHODS;i++) {
57         if (!strcmp(innconf->ovmethod, ov_methods[i].name))
58             break;
59     }
60     if (i == NUM_OV_METHODS) {
61         syslog(L_FATAL, "%s is not found for ovmethod", innconf->ovmethod);
62         fprintf(stderr, "%s is not found for ovmethod\n", innconf->ovmethod);
63         return false;
64     }
65     ov = ov_methods[i];
66     val = (*ov.open)(mode);
67     if (atexit(OVclose) < 0) {
68         OVclose();
69         return false;
70     }
71     if (innconf->ovgrouppat != NULL) {
72         for (i = 1, p = innconf->ovgrouppat; *p && (p = strchr(p+1, ',')); i++);
73         OVnumpatterns = i;
74         OVpatterns = xmalloc(OVnumpatterns * sizeof(char *));
75         for (i = 0, p = strtok(innconf->ovgrouppat, ","); p != NULL && i <= OVnumpatterns ; i++, p = strtok(NULL, ","))
76             OVpatterns[i] = xstrdup(p);
77         if (i != OVnumpatterns) {
78             syslog(L_FATAL, "extra ',' in pattern");
79             fprintf(stderr, "extra ',' in pattern");
80             return false;
81         }
82     }
83     return val;
84 }
85
86 bool
87 OVgroupstats(char *group, int *lo, int *hi, int *count, int *flag)
88 {
89     if (!ov.open) {
90         /* must be opened */
91         syslog(L_ERROR, "ovopen must be called first");
92         fprintf(stderr, "ovopen must be called first");
93         return false;
94     }
95     return ((*ov.groupstats)(group, lo, hi, count, flag));
96 }
97
98 bool
99 OVgroupadd(char *group, ARTNUM lo, ARTNUM hi, char *flag)
100 {
101     /* lomark should never be changed in each ovmethod if lo is 0 */
102     if (!ov.open) {
103         /* must be opened */
104         syslog(L_ERROR, "ovopen must be called first");
105         fprintf(stderr, "ovopen must be called first");
106         return false;
107     }
108     return ((*ov.groupadd)(group, lo, hi, flag));
109 }
110
111 bool
112 OVgroupdel(char *group)
113 {
114     if (!ov.open) {
115         /* must be opened */
116         syslog(L_ERROR, "ovopen must be called first");
117         fprintf(stderr, "ovopen must be called first");
118         return false;
119     }
120     return ((*ov.groupdel)(group));
121 }
122
123 OVADDRESULT
124 OVadd(TOKEN token, char *data, int len, time_t arrived, time_t expires)
125 {
126     char                *next, *nextcheck;
127     static char         *xrefdata, *patcheck, *overdata;
128     char                *xrefstart = NULL;
129     char                *xrefend;
130     static int          xrefdatalen = 0, overdatalen = 0;
131     bool                found = false;
132     int                 xreflen;
133     int                 i;
134     char                *group;
135     ARTNUM              artnum;
136
137     if (!ov.open) {
138         /* must be opened */
139         syslog(L_ERROR, "ovopen must be called first");
140         fprintf(stderr, "ovopen must be called first");
141         return OVADDFAILED;
142     }
143
144     /*
145      * find last Xref: in the overview line.  Note we need to find the *last*
146      * Xref:, since there have been corrupted articles on Usenet with Xref:
147      * fragments stuck in other header lines.  The last Xref: is guaranteed
148      * to be from our server.
149      */
150
151     for (next = data; ((len - (next - data)) > 6 ) && ((next = memchr(next, 'X', len - (next - data))) != NULL); ) {
152         if (memcmp(next, "Xref: ", 6) == 0) {
153             found =  true;
154             xrefstart = next;
155         }
156         next++;
157     }
158
159     if (!found)
160         return OVADDFAILED;
161
162     next = xrefstart;
163     for (i = 0; (i < 2) && (next < (data + len)); i++) {
164         if ((next = memchr(next, ' ', len - (next - data))) == NULL)
165             return OVADDFAILED;
166         next++;
167     }
168     xreflen = len - (next - data);
169
170     /*
171      * If there are other fields beyond Xref in overview, then
172      * we must find Xref's end, or data following is misinterpreted.
173      */
174     if ((xrefend = memchr(next, '\t', xreflen)) != NULL)
175         xreflen = xrefend - next;
176
177     if (xrefdatalen == 0) {
178         xrefdatalen = BIG_BUFFER;
179         xrefdata = xmalloc(xrefdatalen);
180         if (innconf->ovgrouppat != NULL)
181             patcheck = xmalloc(xrefdatalen);
182     }
183     if (xreflen > xrefdatalen) {
184         xrefdatalen = xreflen;
185         xrefdata = xrealloc(xrefdata, xrefdatalen + 1);
186         if (innconf->ovgrouppat != NULL)
187             patcheck = xrealloc(patcheck, xrefdatalen + 1);
188     }
189     if (overdatalen == 0) {
190         overdatalen = BIG_BUFFER;
191         overdata = xmalloc(overdatalen);
192     }
193     if (len + 16 > overdatalen) {
194         overdatalen = len + 16;
195         overdata = xrealloc(overdata, overdatalen);
196     }
197
198     if (innconf->ovgrouppat != NULL) {
199         memcpy(patcheck, next, xreflen);
200         patcheck[xreflen] = '\0';
201         for (group = patcheck; group && *group; group = memchr(nextcheck, ' ', xreflen - (nextcheck - patcheck))) {
202             while (isspace((int)*group))
203                 group++;
204             if ((nextcheck = memchr(group, ':', xreflen - (patcheck - group))) == NULL)
205                 return OVADDFAILED;
206             *nextcheck++ = '\0';
207             if (!OVgroupmatch(group)) {
208                 if (!SMprobe(SELFEXPIRE, &token, NULL) && innconf->groupbaseexpiry)
209                     /* this article will never be expired, since it does not
210                        have self expiry function in stored method and
211                        groupbaseexpiry is true */
212                     return OVADDFAILED;
213                 return OVADDGROUPNOMATCH;
214             }
215         }
216     }
217     memcpy(xrefdata, next, xreflen);
218     xrefdata[xreflen] = '\0';
219     for (group = xrefdata; group && *group; group = memchr(next, ' ', xreflen - (next - xrefdata))) {
220         /* Parse the xref part into group name and article number */
221         while (isspace((int)*group))
222             group++;
223         if ((next = memchr(group, ':', xreflen - (group - xrefdata))) == NULL)
224             return OVADDFAILED;
225         *next++ = '\0';
226         artnum = atoi(next);
227         if (artnum <= 0)
228             continue;
229
230         sprintf(overdata, "%ld\t", artnum);
231         i = strlen(overdata);
232         memcpy(overdata + i, data, len);
233         i += len;
234         memcpy(overdata + i, "\r\n", 2);
235         i += 2;
236
237         if(! (*ov.add)(group, artnum, token, overdata, i, arrived, expires))
238             return OVADDFAILED;
239     }
240
241     return OVADDCOMPLETED;
242 }
243
244 bool
245 OVcancel(TOKEN token)
246 {
247     if (!ov.open) {
248         /* must be opened */
249         syslog(L_ERROR, "ovopen must be called first");
250         fprintf(stderr, "ovopen must be called first");
251         return false;
252     }
253     return ((*ov.cancel)(token));
254 }
255
256 void *
257 OVopensearch(char *group, int low, int high)
258 {
259     if (!ov.open) {
260         /* must be opened */
261         syslog(L_ERROR, "ovopen must be called first");
262         fprintf(stderr, "ovopen must be called first");
263         return false;
264     }
265     return ((*ov.opensearch)(group, low, high));
266 }
267
268 bool
269 OVsearch(void *handle, ARTNUM *artnum, char **data, int *len, TOKEN *token,
270          time_t *arrived)
271 {
272     if (!ov.open) {
273         /* must be opened */
274         syslog(L_ERROR, "ovopen must be called first");
275         fprintf(stderr, "ovopen must be called first");
276         return false;
277     }
278     return ((*ov.search)(handle, artnum, data, len, token, arrived));
279 }
280
281 void
282 OVclosesearch(void *handle)
283 {
284     if (!ov.open) {
285         /* must be opened */
286         syslog(L_ERROR, "ovopen must be called first");
287         fprintf(stderr, "ovopen must be called first");
288         return;
289     }
290     (*ov.closesearch)(handle);
291     return;
292 }
293
294 bool
295 OVgetartinfo(char *group, ARTNUM artnum, TOKEN *token)
296 {
297     if (!ov.open) {
298         /* must be opened */
299         syslog(L_ERROR, "ovopen must be called first");
300         fprintf(stderr, "ovopen must be called first");
301         return false;
302     }
303     return ((*ov.getartinfo)(group, artnum, token));
304 }
305
306 bool
307 OVexpiregroup(char *group, int *lo, struct history *h)
308 {
309     if (!ov.open) {
310         /* must be opened */
311         syslog(L_ERROR, "ovopen must be called first");
312         fprintf(stderr, "ovopen must be called first");
313         return false;
314     }
315     return ((*ov.expiregroup)(group, lo, h));
316 }
317
318 bool
319 OVctl(OVCTLTYPE type, void *val)
320 {
321     if (!ov.open) {
322         /* must be opened */
323         syslog(L_ERROR, "ovopen must be called first");
324         fprintf(stderr, "ovopen must be called first");
325         return false;
326     }
327     switch (type) {
328     case OVGROUPBASEDEXPIRE:
329         if (!innconf->groupbaseexpiry) {
330             syslog(L_ERROR, "OVGROUPBASEDEXPIRE is not allowed if groupbaseexpiry if false");
331             fprintf(stderr, "OVGROUPBASEDEXPIRE is not allowed if groupbaseexpiry if false");
332             return false;
333         }
334         if (((OVGE *)val)->delayrm) {
335             if ((((OVGE *)val)->filename == NULL) || (strlen(((OVGE *)val)->filename) == 0)) {
336                 syslog(L_ERROR, "file name must be specified");
337                 fprintf(stderr, "file name must be specified");
338                 return false;
339             }
340             if ((EXPunlinkfile = fopen(((OVGE *)val)->filename, "w")) == NULL) {
341                 syslog(L_ERROR, "fopen: %s failed: %m", ((OVGE *)val)->filename);
342                 fprintf(stderr, "fopen: %s failed: %s", ((OVGE *)val)->filename, 
343                               strerror(errno));
344                 return false;
345             }
346         }
347         OVdelayrm = ((OVGE *)val)->delayrm;
348         OVusepost = ((OVGE *)val)->usepost;
349         OVrealnow = ((OVGE *)val)->now;
350         OVnow = ((OVGE *)val)->now + (time_t)((OVGE *)val)->timewarp;
351         OVquiet = ((OVGE *)val)->quiet;
352         OVkeep = ((OVGE *)val)->keep;
353         OVearliest = ((OVGE *)val)->earliest;
354         OVignoreselfexpire = ((OVGE *)val)->ignoreselfexpire;
355         return true;
356     case OVSTATALL:
357         OVstatall = *(bool *)val;
358         return true;
359     default:
360         return ((*ov.ctl)(type, val));
361     }
362 }
363
364 void
365 OVclose(void)
366 {
367     if (!ov.open)
368         return;
369     (*ov.close)();
370     memset(&ov, '\0', sizeof(ov));
371     OVEXPcleanup();
372 }