chiark / gitweb /
fix warnings
[nntp-merge-chiark.git] / config.c
1 /**/
2
3 /* Algorithm for finding a group:
4  *  OUT OF DATE !
5  * Start at listentry 0.
6  * For each character,
7  *  keep going through list until one of the following:
8  *   if character in string matches in list entry,
9  *    then jump to that listentry's `p' and go on to the next character
10  *   if we encounter a null character (ie, default, end of this list)
11  *    then we go to the listentry's `p' and use the `gi' member of
12  *    the union as the group info struct
13  * If we run out of characters,
14  *  keep going through the list until we find a default or an if-terminate-here
15  *  use the listentry's `p', and the `gi' member of that entry's union.
16  *
17  * The listentry pointingto's are indices into the array of listentry unions.
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <assert.h>
23 #include <ctype.h>
24 #include <string.h>
25 #include <limits.h>
26 #include <sys/types.h>
27 #include <arpa/inet.h>
28 #include <netinet/in.h>
29
30 #include "nntp-merge.h"
31
32 /* Conceptually we have one array with all the stuff in it.  However,
33  * this is wasteful of space (we want the lookup algorithm and its
34  * data to fit in 1K).  Conceptually the array is one of
35  *  struct {
36  *    char c;
37  *    unsigned short n_to_go_to_if_char_matches_p;
38  *    struct groupinfo *groupinfo_to_use_if_char_does_not_match;
39  *  };
40  * The groupinfo pointer is in the array element corresponding to the
41  * null character at the end of the list.
42  * Conceptually `end of string' is just another character, 1, except
43  * that when you find it in the table you use the groupinfo member rather
44  * then n_to_go member.
45  */
46
47 struct tnode {
48   unsigned short lip;
49   struct tlistentry {
50     struct tlistentry *next;
51     char c;
52     struct tnode *p;
53   } *l;
54   struct groupinfo *ifdef, *ifterm;
55 };
56
57 void *xmalloc(size_t s) {
58   void *r;
59   r= malloc(s);
60   if (!r) die("malloc failed");
61   return r;
62 }
63
64 char *xstrdup(const char *s) {
65   char *r;
66   r= xmalloc(strlen(s)+1);
67   strcpy(r,s);
68   return r;
69 }
70
71 struct permission *permissions;
72 struct permission *restrictpost;
73 struct serverinfo *servers;
74 static struct serverinfo **lastserver;
75
76 static char *lisc=0;
77 static unsigned short *lisp=0;
78 static struct groupinfo **lisg=0;
79
80 static int line;
81 static int nrequired;
82
83 static struct tnode *newemptytnode(void) {
84   static struct tnode *r;
85
86   r= xmalloc(sizeof(struct tnode));
87   r->l= 0;
88   r->ifdef= 0;
89   r->ifterm= 0;
90   return r;
91 }
92
93 static void setmissingdefaults(struct tnode *n, struct groupinfo *gi) {
94   struct tlistentry *tle;
95   if (n->ifdef) gi= n->ifdef; else n->ifdef= gi;
96   for (tle= n->l; tle; tle= tle->next) setmissingdefaults(tle->p,gi);
97 }
98   
99 static void syntax(const char *m) {
100   printf("400 syntax error in config file, line %d: %s\r\n",line,m);
101   exit(1);
102 }
103
104 static void countup(struct tnode *n) {
105   struct tlistentry *tle;
106
107   assert(n->ifdef);
108   n->lip= nrequired;
109   for (tle= n->l; tle; tle= tle->next) nrequired++;
110   if (n->ifterm) nrequired++;
111   nrequired++;
112
113   for (tle= n->l; tle; tle= tle->next) countup(tle->p);
114 }
115
116 static void fillin(struct tnode *n) {
117   struct tlistentry *tle;
118   int i;
119
120   i= n->lip;
121   for (tle= n->l; tle; tle= tle->next) {
122     lisc[i]= tle->c;
123     lisp[i]= tle->p->lip;
124     fillin(tle->p);
125     i++;
126   }
127   if (n->ifterm) {
128     lisc[i]= '\1';
129     lisg[i]= n->ifterm;
130     i++;
131   }
132   lisc[i]= 0;
133   lisg[i]= n->ifdef;
134 }
135
136 static void posscopy(int *needcopyp, struct groupinfo **gip) {
137   struct groupinfo *gi;
138   if (!*needcopyp) return;
139   gi= xmalloc(sizeof(struct groupinfo));
140   *gi= **gip;
141   *needcopyp= 0;
142   *gip= gi;
143   return;
144 }
145
146 struct permission *findpermit(const char *name) {
147   struct permission *pi;
148
149   for (pi= permissions; pi && strcmp(name,pi->name); pi= pi->next);
150   return pi;
151 }
152
153 static struct permission *findmakepermit(const char *name) {
154   struct permission *pi;
155
156   if (!strcmp(name,"*")) return 0;
157   pi= findpermit(name); if (pi) return pi;
158
159   pi= xmalloc(sizeof(struct permission));
160   pi->name= xstrdup(name);
161   pi->authd= 0;
162   pi->next= permissions;
163   permissions= pi;
164   return pi;
165 }
166
167 static struct serverinfo *findserver(const char *name) {
168   struct serverinfo *search;
169
170   for (search= servers;
171        search && strcmp(search->nickname,name);
172        search= search->next);
173
174   if (!search) syntax("unknown server nickname");
175   return search;
176 }
177
178 static struct tnode *ttree;
179
180 void readconfig(void) {
181   FILE *file;
182   struct groupinfo *gi;
183   int needcopy= 0;
184   struct tnode *tcur;
185   struct tlistentry *tle;
186   char buf[MAX_COMMAND], *p, *q;
187   struct serverinfo *si;
188   int c, i, insearchser;
189   unsigned long portt;
190
191   file= fopen("nntp-merge.conf","r");
192   if (!file) die("unable to open config file");
193
194   gi= xmalloc(sizeof(struct groupinfo));
195   gi->readfrom= 0;
196   gi->postto[0]= 0;
197   gi->offset= 0;
198   gi->restrictto= 0;
199   gi->readonlyto= 0;
200   needcopy= 0;
201
202   ttree= newemptytnode();
203   line= 0;
204   servers= 0;
205   lastserver= &servers;
206   while ((c= getc(file)) != EOF) {
207     line++;
208     if (c == '\n') continue;
209     if (c == '#') {
210       while ((c= getc(file)) != EOF && c != '\n');
211       continue;
212     }
213     if (isspace(c)) {
214       if (!gi->readfrom) syntax("config file has groups before readfrom specs");
215       do { c= getc(file); } while (c != EOF && c != '\n' && isspace(c));
216       if (c == '\n') continue;
217       ungetc(c,file);
218       tcur= ttree;
219       while ((c= getc(file)) != EOF && !isspace(c) && c != '*') {
220         if (c == '\1') syntax("group name in config file has ^A character");
221         for (tle= tcur->l; tle && tle->c != c; tle= tle->next);
222         if (!tle) {
223           tle= xmalloc(sizeof(struct tlistentry));
224           tle->next= tcur->l;
225           tle->c= c;
226           tle->p= newemptytnode();
227           tcur->l= tle;
228         }
229         tcur= tle->p;
230       }
231       if (c == '*') {
232         tcur->ifdef= gi;
233         needcopy= 1;
234         c= getc(file);
235       } else {
236         while (c != EOF && c != '\n' && isspace(c)) c= getc(file);
237         if (c != EOF && (isdigit(c) || c == '-')) {
238           posscopy(&needcopy,&gi);
239           ungetc(c,file);
240           if (fscanf(file,"%d",&gi->offset) != 1) die("fscanf in group offset");
241         } else if (gi->offset) {
242           posscopy(&needcopy,&gi);
243           gi->offset= 0;
244         }
245         tcur->ifterm= gi;
246         needcopy= 1;
247       }
248       while (c != EOF && c != '\n') {
249         c= getc(file);
250         if (!isspace(c)) syntax("stuff after * or space (and offset?) in group");
251       }
252     } else {
253       ungetc(c,file);
254       if (!fgets(buf,sizeof(buf),file)) break;
255       p= strchr(buf,'\n'); if (p) *p= 0;
256       p= strtok(buf," \t");
257       if (!strcmp(p,"read")) {
258         p= strtok(0," \t"); if (!p) syntax("missing server after read");
259         posscopy(&needcopy,&gi);
260         gi->readfrom= findserver(p);
261       } else if (!strcmp(p,"post")) {
262         posscopy(&needcopy,&gi);
263         for (i=0; (p= strtok(0," \t")); i++) {
264           if (i >= sizeof(gi->postto)/sizeof(gi->postto[0])-1)
265             syntax("too many servers after post");
266           gi->postto[i]= findserver(p);
267         }
268         gi->postto[i]= 0;
269       } else if (!strcmp(p,"server") || !strcmp(p,"server-nosearch")) {
270         insearchser= !strcmp(p,"server");
271         p= strtok(0," \t"); if (!p) syntax("missing server name");
272         for (si= servers; si && strcmp(si->nickname,p); si= si->next);
273         if (si) syntax("server nickname redefined");
274         si= xmalloc(sizeof(struct serverinfo));
275         si->next= 0;
276         si->searchthis= insearchser;
277         si->nickname= xstrdup(p);
278         si->send= 0;
279         p= strtok(0," \t"); if (!p) syntax("missing server host:port/command");
280         if (*p == '/') {
281           si->hostname= xstrdup(p);
282           si->port= 0;
283           si->program= 1;
284         } else {
285           q= strchr(p,':'); if (!q || !*q) syntax("missing :port in server");
286           *q++= 0;
287           si->hostname= xstrdup(p);
288           portt= strtoul(q,&q,10);
289           if (portt > USHRT_MAX || *q) syntax("bad :port in server");
290           si->port= portt;
291           si->program= 0;
292         }
293         while ((p= strtok(0," \t"))) {
294           if (strcmp(p,"mode-reader")) syntax("bad option on server");
295           si->send= "MODE READER";
296         }
297         si->rfile= si->wfile= 0;
298         *lastserver= si;
299         lastserver= &si->next;
300       } else if (!strcmp(p,"permit")) {
301         posscopy(&needcopy,&gi);
302         p= strtok(0," \t");
303         if (!p) syntax("missing read-only group after permit");
304         gi->readonlyto= findmakepermit(p);
305         p= strtok(0," \t");
306         if (!p) syntax("missing read-write group after permit");
307         gi->restrictto= findmakepermit(p);
308       } else if (!strcmp(p,"myfqdn")) {
309         posscopy(&needcopy,&gi);
310         p= strtok(0," \t");
311         if (!p) syntax("missing fqdn after myfqdn");
312         free(myfqdn);
313         myfqdn= xstrdup(p);
314       } else if (!strcmp(p,"xref")) {
315         posscopy(&needcopy,&gi);
316         p= strtok(0," \t");
317         if (!p) syntax("missing fqdn after myfqdn");
318         free(myxref);
319         myxref= xstrdup(p);
320       } else if (!strcmp(p,"fetch") ||
321                  !strcmp(p,"believe") ||
322                  !strcmp(p,"minreaddays") ||
323                  !strcmp(p,"maxperuser") ||
324                  !strcmp(p,"extrarc") ||
325                  !strcmp(p,"ignorerc")) {
326         /* These aren't for us, we ignore them. */
327       } else {
328         syntax("unknown thing in file");
329       }
330     }
331   }
332   if (ferror(file)) die("failed to read config file");
333   if (!ttree->ifdef) syntax("missing default newsgroup entry");
334   fclose(file);
335
336   setmissingdefaults(ttree,0);
337   
338   /* Now we count the total number of entries we need in our table. */
339   nrequired= 0;
340   countup(ttree);
341   lisc= xmalloc(nrequired);
342   lisp= xmalloc(nrequired*sizeof(unsigned short));
343   lisg= xmalloc(nrequired*sizeof(struct groupinfo*));
344   fillin(ttree);
345 }
346
347 struct groupinfo *findgroup(const char *name) {
348   /* Null or ':' terminated */
349   int c, d;
350   unsigned short cnode;
351
352   cnode= 0;
353   while ((c= *name++) && c != ':' && !isspace(c)) {
354     while ((d= lisc[cnode]) && d != c) cnode++;
355     if (d) { cnode= lisp[cnode]; continue; }
356     return lisg[cnode];
357   }
358   while ((d= lisc[cnode]) && d != '\1') cnode++;
359   return lisg[cnode];
360 }