chiark / gitweb /
run debian/rules patch
[inn-innduct.git] / nnrpd / newnews.c
1 /*  $Revision: 6372 $
2 **
3 **  The newnews command.
4 */
5 #include "config.h"
6 #include "clibrary.h"
7
8 #include "inn/innconf.h"
9 #include "inn/messages.h"
10 #include "inn/wire.h"
11 #include "nnrpd.h"
12 #include "ov.h"
13 #include "cache.h"
14
15 #define GROUP_LIST_DELTA        10
16
17 static bool FindHeader(ARTHANDLE *art, const char **pp, const char **qp,
18     const char* hdr, size_t hdrlen)
19 {
20   const char *p, *p1, *q;
21   bool Nocr = true;
22
23   p = wire_findheader(art->data, art->len, hdr);
24   if (p == NULL)
25     return false;
26   q = p;
27   for (p1 = NULL; p < art->data + art->len; p++) {
28     if (p1 != NULL && *p1 == '\r' && *p == '\n') {
29       Nocr = false;
30       break;
31     }
32     if (*p == '\n') {
33       Nocr = true;
34       break;
35     }
36     p1 = p;
37   }
38   if (p >= art->data + art->len)
39     return false;
40   if (!Nocr)
41     p = p1;
42
43   *pp = p;
44   *qp = q;
45   return true;
46 }
47
48 /*
49 **  get Xref header
50 */
51 static char *GetXref(ARTHANDLE *art) {
52   const char    *p, *q;
53
54   if (!FindHeader(art, &p, &q, "xref", sizeof("xref")))
55     return NULL;
56   return xstrndup(q, p - q);
57 }
58
59 /*
60 **  Split newsgroup list into array of newsgroups.  Return static pointer,
61 **  or NULL if there are no newsgroup.
62 */
63 static char **GetGroups(char *p) {
64   static int    size;
65   static char   **list;
66   int           i;
67   char          *q;
68   static char   *Xrefbuf = NULL;
69   char          *Xref = p;
70
71   if (size == 0) {
72     size = GROUP_LIST_DELTA;
73     list = xmalloc((size + 1) * sizeof(char *));
74   }
75   Xref = p;
76   for (Xref++; *Xref == ' '; Xref++);
77   if ((Xref = strchr(Xref, ' ')) == NULL)
78     return NULL;
79   for (Xref++; *Xref == ' '; Xref++);
80   if (!Xrefbuf)
81     Xrefbuf = xmalloc(BIG_BUFFER);
82   strlcpy(Xrefbuf, Xref, BIG_BUFFER);
83   if ((q = strchr(Xrefbuf, '\t')) != NULL)
84     *q = '\0';
85   p = Xrefbuf;
86
87   for (i = 0 ; ;i++) {
88     while (ISWHITE(*p))
89       p++;
90     if (*p == '\0' || *p == '\n')
91       break;
92
93     if (i >= size - 1) {
94       size += GROUP_LIST_DELTA;
95       list = xrealloc(list, (size + 1) * sizeof(char *));
96     }
97     for (list[i] = p; *p && *p != '\n' && !ISWHITE(*p); p++) {
98       if (*p == ':')
99         *p = '\0';
100     }
101     if (*p) *p++ = '\0';
102   }
103   list[i] = NULL;
104   return i ? list : NULL;
105 }
106
107 static bool HaveSeen(bool AllGroups, char *group, char **groups, char **xrefs) {
108   char *list[2];
109
110   list[1] = NULL;
111   for ( ; *xrefs; xrefs++) {
112     list[0] = *xrefs;
113     if ((!AllGroups && PERMmatch(groups, list)) && (!PERMspecified || (PERMspecified && PERMmatch(PERMreadlist, list)))) {
114       if (!strcmp(*xrefs, group))
115         return false;
116       else
117         return true;
118     }
119   }
120   return false;
121 }
122
123 static char **groups;
124
125 static void
126 process_newnews(char *group, bool AllGroups, time_t date)
127 {
128     char **xrefs;
129     int count;
130     void *handle;
131     char *p;
132     time_t arrived;
133     ARTHANDLE *art = NULL;
134     TOKEN token;
135     char *data;
136     int len;
137     char *grplist[2];
138     time_t now;
139
140     grplist[0] = group;
141     grplist[1] = NULL;
142     if (PERMspecified && !PERMmatch(PERMreadlist, grplist))
143         return;
144     if (!AllGroups && !PERMmatch(groups, grplist))
145         return;
146     if (!OVgroupstats(group, &ARTlow, &ARThigh, &count, NULL))
147         return;
148     if ((handle = OVopensearch(group, ARTlow, ARThigh)) != NULL) {
149         ARTNUM artnum;
150         unsigned long artcount = 0;
151         struct cvector *vector = NULL;
152
153         if (innconf->nfsreader) {
154             time(&now);
155             /* move the start time back nfsreaderdelay seconds */
156             if (date >= innconf->nfsreaderdelay)
157                 date -= innconf->nfsreaderdelay;
158         }
159         while (OVsearch(handle, &artnum, &data, &len, &token, &arrived)) {
160             if (innconf->nfsreader && arrived + innconf->nfsreaderdelay > now)
161                 continue;
162             if (len == 0 || date > arrived)
163                 continue;
164
165             vector = overview_split(data, len, NULL, vector);
166             if (overhdr_xref == -1) {
167                 if ((art = SMretrieve(token, RETR_HEAD)) == NULL)
168                     continue;
169                 p = GetXref(art);
170                 SMfreearticle(art);
171             } else {
172                 if (PERMaccessconf->nnrpdcheckart && 
173                     !ARTinstorebytoken(token))
174                     continue;
175                 /* We only care about the newsgroup list here, virtual
176                  * hosting isn't relevant */
177                 p = overview_getheader(vector, overhdr_xref, OVextra);
178             }
179             if (p == NULL)
180                 continue;
181             xrefs = GetGroups(p);
182             free(p);
183             if (xrefs == NULL)
184                 continue;
185             if (HaveSeen(AllGroups, group, groups, xrefs))
186                 continue;
187             p = overview_getheader(vector, OVERVIEW_MESSAGE_ID, OVextra);
188             if (p == NULL)
189                 continue;
190
191             ++artcount;
192             cache_add(HashMessageID(p), token);
193             Printf("%s\r\n", p);
194             free(p);
195         }
196         OVclosesearch(handle);
197         notice("%s newnews %s %lu", ClientHost, group, artcount);
198         if (vector)
199             cvector_free(vector);
200     }
201 }
202
203 /*
204 **  NEWNEWS newsgroups date time ["GMT"]
205 **  Return the Message-ID of any articles after the specified date
206 */
207 void CMDnewnews(int ac, char *av[]) {
208   char          *p, *q;
209   char          *path;
210   bool          AllGroups;
211   char          line[BIG_BUFFER];
212   time_t        date;
213   QIOSTATE      *qp;
214   int           i;
215   bool          local;
216
217   if (!PERMaccessconf->allownewnews) {
218       Reply("%d NEWNEWS command disabled by administrator\r\n", NNTP_ACCESS_VAL);
219       return;
220   }
221
222   if (!PERMcanread) {
223       Reply("%s\r\n", NNTP_ACCESS);
224       return;
225   }
226
227   /* Make other processes happier if someone uses NEWNEWS */
228   if (innconf->nicenewnews > 0) {
229       nice(innconf->nicenewnews);
230       innconf->nicenewnews = 0;
231   }
232
233   snprintf(line, sizeof(line), "%s %s %s %s", av[1], av[2], av[3],
234            (ac >= 5 && (*av[4] == 'G' || *av[4] == 'U')) ? "GMT" : "local");
235   notice("%s newnews %s", ClientHost, line);
236
237   TMRstart(TMR_NEWNEWS);
238   /* Optimization in case client asks for !* (no groups) */
239   if (strcmp(av[1], "!*") == 0) {
240       Reply("%s\r\n", NNTP_NEWNEWSOK);
241       Printf(".\r\n");
242       TMRstop(TMR_NEWNEWS);
243       return;
244   }
245
246   /* Parse the newsgroups. */
247   AllGroups = (strcmp(av[1], "*") == 0);
248   if (!AllGroups && !NGgetlist(&groups, av[1])) {
249       Reply("%d Bad newsgroup specifier %s\r\n", NNTP_SYNTAX_VAL, av[1]);
250       TMRstop(TMR_NEWNEWS);
251       return;
252   }
253
254   /* Parse the date. */
255   local = !(ac > 4 && strcasecmp(av[4], "GMT") == 0);
256   date = parsedate_nntp(av[2], av[3], local);
257   if (date == (time_t) -1) {
258       Reply("%d Bad date\r\n", NNTP_SYNTAX_VAL);
259       TMRstop(TMR_NEWNEWS);
260       return;
261   }
262
263   if (strcspn(av[1], "\\!*[?]") == strlen(av[1])) {
264       /* optimise case - don't need to scan the active file pattern
265        * matching */
266       Reply("%s\r\n", NNTP_NEWNEWSOK);
267       for (i = 0; groups[i]; ++i) {
268           process_newnews(groups[i], AllGroups, date);
269       }
270   } else {
271       path = concatpath(innconf->pathdb, _PATH_ACTIVE);
272       qp = QIOopen(path);
273       if (qp == NULL) {
274           if (errno == ENOENT) {
275               Reply("%d Can't open active\r\n", NNTP_TEMPERR_VAL);
276           } else {
277               syswarn("%s cant fopen %s", ClientHost, path);
278               Reply("%d Can't open active\r\n", NNTP_TEMPERR_VAL);
279           }
280           free(path);
281           TMRstop(TMR_NEWNEWS);
282           return;
283       }
284       free(path);
285
286       Reply("%s\r\n", NNTP_NEWNEWSOK);
287
288       while ((p = QIOread(qp)) != NULL) {
289           for (q = p; *q != '\0'; q++) {
290               if (*q == ' ' || *q == '\t') {
291                   *q = '\0';
292                   break;
293               }
294           }
295           process_newnews(p, AllGroups, date);
296       }
297       QIOclose(qp);
298   }
299   Printf(".\r\n");
300   TMRstop(TMR_NEWNEWS);
301 }