]> Pileus Git - ~andy/fetchmail/blob - rcfile_y.y
Remodularize so the parser is more self-contained.
[~andy/fetchmail] / rcfile_y.y
1 %{
2 /*
3  * rcfile_y.y -- Run control file parser for fetchmail
4  *
5  * For license terms, see the file COPYING in this directory.
6  */
7
8 #include "config.h"
9 #include <stdio.h>
10 #include <sys/types.h>
11 #include <sys/file.h>
12 #include <sys/wait.h>
13 #include <sys/stat.h>
14 #include <errno.h>
15 #if defined(STDC_HEADERS)
16 #include <stdlib.h>
17 #endif
18 #if defined(HAVE_UNISTD_H)
19 #include <unistd.h>
20 #endif
21 #include <string.h>
22
23 #include "fetchmail.h"
24
25 /* parser reads these */
26 char *rcfile;                   /* path name of rc file */
27 struct query cmd_opts;          /* where to put command-line info */
28
29 /* parser sets these */
30 int poll_interval;              /* poll interval in seconds */
31 char *logfile;                  /* log file for daemon mode */
32 bool use_syslog;                /* if syslog was set */
33 struct query *querylist;        /* head of server list (globally visible) */
34
35 int yydebug;                    /* in case we didn't generate with -- debug */
36
37 static struct query current;    /* current server record */
38 static int prc_errflag;
39
40 static void record_current();
41 static void user_reset();
42 static int reset_server(char *name, int skip);
43
44 /* using Bison, this arranges that yydebug messages will show actual tokens */
45 extern char * yytext;
46 #define YYPRINT(fp, type, val)  fprintf(fp, " = \"%s\"", yytext)
47 %}
48
49 %union {
50   int proto;
51   int number;
52   char *sval;
53 }
54
55 %token DEFAULTS POLL SKIP AKA LOCALDOMAINS PROTOCOL
56 %token AUTHENTICATE TIMEOUT KPOP KERBEROS4
57 %token ENVELOPE USERNAME PASSWORD FOLDER SMTPHOST MDA PRECONNECT LIMIT
58 %token IS HERE THERE TO MAP WILDCARD
59 %token SET BATCHLIMIT FETCHLIMIT LOGFILE DAEMON SYSLOG INTERFACE MONITOR
60 %token <proto> PROTO
61 %token <sval>  STRING
62 %token <number> NUMBER
63 %token NO KEEP FLUSH FETCHALL REWRITE FORCECR STRIPCR PASS8BITS 
64 %token DNS PORT UIDL INTERVAL
65
66 %%
67
68 rcfile          : /* empty */
69                 | statement_list
70                 ;
71
72 statement_list  : statement
73                 | statement_list statement
74                 ;
75
76 /* future global options should also have the form SET <name> <value> */
77 statement       : SET LOGFILE MAP STRING        {logfile = xstrdup($4);}
78                 | SET DAEMON NUMBER             {poll_interval = $3;}
79                 | SET SYSLOG                    {use_syslog = TRUE;}
80
81 /* 
82  * The way the next two productions are written depends on the fact that
83  * userspecs cannot be empty.  It's a kluge to deal with files that set
84  * up a load of defaults and then have poll statements following with no
85  * user options at all. 
86  */
87                 | define_server serverspecs             {record_current();}
88                 | define_server serverspecs userspecs
89                 ;
90
91 define_server   : POLL STRING   {
92                                     if (!reset_server($2, FALSE))
93                                     {
94                                         yyerror("duplicate entry name not allowed");
95                                         YYERROR;
96                                     }
97                                 }
98                 | SKIP STRING   {
99                                     if (!reset_server($2, TRUE))
100                                     {
101                                         yyerror("duplicate entry name not allowed");
102                                         YYERROR;
103                                     }
104                                 }
105                 | DEFAULTS      {
106                                     if (!reset_server("defaults", FALSE))
107                                     {
108                                         yyerror("can't have two default entries");
109                                         YYERROR;
110                                     }
111                                 }
112                 ;
113
114 serverspecs     : /* EMPTY */
115                 | serverspecs serv_option
116                 ;
117
118 alias_list      : STRING                {save_str(&current.server.names,-1,$1);}
119                 | alias_list STRING     {save_str(&current.server.names,-1,$2);}
120                 ;
121
122 domain_list     : STRING                {save_str(&current.server.localdomains,-1,$1);}
123                 | domain_list STRING    {save_str(&current.server.localdomains,-1,$2);}
124                 ;
125
126 serv_option     : AKA alias_list
127                 | LOCALDOMAINS domain_list
128                 | PROTOCOL PROTO        {current.server.protocol = $2;}
129                 | PROTOCOL KPOP         {
130                                             current.server.protocol = P_POP3;
131                                             current.server.preauthenticate = A_KERBEROS_V4;
132                                             current.server.port = KPOP_PORT;
133                                         }
134                 | UIDL                  {current.server.uidl = FLAG_TRUE;}
135                 | NO UIDL               {current.server.uidl  = FLAG_FALSE;}
136                 | PORT NUMBER           {current.server.port = $2;}
137                 | INTERVAL NUMBER               {current.server.interval = $2;}
138                 | AUTHENTICATE PASSWORD {current.server.preauthenticate = A_PASSWORD;}
139                 | AUTHENTICATE KERBEROS4        {current.server.preauthenticate = A_KERBEROS_V4;}
140                 | TIMEOUT NUMBER        {current.server.timeout = $2;}
141                 | ENVELOPE STRING       {current.server.envelope = xstrdup($2);}
142                 | INTERFACE STRING      {
143 #ifdef linux
144                                         interface_parse($2, &current.server);
145 #else
146                                         fprintf(stderr, "fetchmail: interface option is only supported under Linux\n");
147 #endif /* linux */
148                                         }
149                 | MONITOR STRING        {
150 #ifdef linux
151                                         current.server.monitor = xstrdup($2);
152 #else
153                                         fprintf(stderr, "fetchmail: monitor option is only supported under Linux\n");
154 #endif /* linux */
155                                         }
156                 | DNS                   {current.server.dns = FLAG_TRUE;}
157                 | NO DNS                {current.server.dns = FLAG_FALSE;}
158                 | NO ENVELOPE           {current.server.envelope = STRING_DISABLED;}
159                 ;
160
161 userspecs       : user1opts             {record_current(); user_reset();}
162                 | explicits
163                 ;
164
165 explicits       : explicitdef           {record_current(); user_reset();}
166                 | explicits explicitdef {record_current(); user_reset();}
167                 ;
168
169 explicitdef     : userdef user0opts
170                 ;
171
172 userdef         : USERNAME STRING       {current.remotename = xstrdup($2);}
173                 | USERNAME mapping_list HERE
174                 | USERNAME STRING THERE {current.remotename = xstrdup($2);}
175                 ;
176
177 user0opts       : /* EMPTY */
178                 | user0opts user_option
179                 ;
180
181 user1opts       : user_option
182                 | user1opts user_option
183                 ;
184
185 localnames      : WILDCARD              {current.wildcard =  TRUE;}
186                 | mapping_list          {current.wildcard =  FALSE;}
187                 | mapping_list WILDCARD {current.wildcard =  TRUE;}
188                 ;
189
190 mapping_list    : mapping               
191                 | mapping_list mapping
192                 ;
193
194 mapping         : STRING        
195                                 {save_str_pair(&current.localnames, $1, NULL);}
196                 | STRING MAP STRING
197                                 {save_str_pair(&current.localnames, $1, $3);}
198                 ;
199
200 folder_list     : STRING                {save_str(&current.mailboxes,-1,$1);}
201                 | folder_list STRING    {save_str(&current.mailboxes,-1,$2);}
202                 ;
203
204 smtp_list       : STRING                {save_str(&current.smtphunt, -1, $1);}
205                 | smtp_list STRING      {save_str(&current.smtphunt, -1, $2);}
206                 ;
207
208 user_option     : TO localnames HERE
209                 | TO localnames
210                 | IS localnames HERE
211                 | IS localnames
212
213                 | IS STRING THERE       {current.remotename = xstrdup($2);}
214                 | PASSWORD STRING       {current.password   = xstrdup($2);}
215                 | FOLDER folder_list
216                 | SMTPHOST smtp_list
217                 | MDA STRING            {current.mda        = xstrdup($2);}
218                 | PRECONNECT STRING     {current.preconnect = xstrdup($2);}
219
220                 | KEEP                  {current.keep       = FLAG_TRUE;}
221                 | FLUSH                 {current.flush      = FLAG_TRUE;}
222                 | FETCHALL              {current.fetchall   = FLAG_TRUE;}
223                 | REWRITE               {current.rewrite    = FLAG_TRUE;}
224                 | FORCECR               {current.forcecr    = FLAG_TRUE;}
225                 | STRIPCR               {current.stripcr    = FLAG_TRUE;}
226                 | PASS8BITS             {current.pass8bits  = FLAG_TRUE;}
227
228                 | NO KEEP               {current.keep       = FLAG_FALSE;}
229                 | NO FLUSH              {current.flush      = FLAG_FALSE;}
230                 | NO FETCHALL           {current.fetchall   = FLAG_FALSE;}
231                 | NO REWRITE            {current.rewrite    = FLAG_FALSE;}
232                 | NO FORCECR            {current.forcecr    = FLAG_FALSE;}
233                 | NO STRIPCR            {current.stripcr    = FLAG_FALSE;}
234
235                 | LIMIT NUMBER          {current.limit      = $2;}
236                 | FETCHLIMIT NUMBER     {current.fetchlimit = $2;}
237                 | BATCHLIMIT NUMBER     {current.batchlimit = $2;}
238                 ;
239 %%
240
241 /* lexer interface */
242 extern char *rcfile;
243 extern int prc_lineno;
244 extern char *yytext;
245 extern FILE *yyin;
246
247 static struct query *hosttail;  /* where to add new elements */
248
249 void yyerror (const char *s)
250 /* report a syntax error */
251 {
252     error_at_line( 0, 0, rcfile, prc_lineno, "%s at %s", s, 
253                    (yytext && yytext[0]) ? yytext : "end of input");
254     prc_errflag++;
255 }
256
257 int prc_filecheck(pathname)
258 /* check that a configuration file is secure */
259 const char *pathname;           /* pathname for the configuration file */
260 {
261     struct stat statbuf;
262
263     errno = 0;
264
265     /* special cases useful for debugging purposes */
266     if (strcmp("/dev/null", pathname) == 0)
267         return(0);
268
269     /* the run control file must have the same uid as the REAL uid of this 
270        process, it must have permissions no greater than 600, and it must not 
271        be a symbolic link.  We check these conditions here. */
272
273     if (lstat(pathname, &statbuf) < 0) {
274         if (errno == ENOENT) 
275             return(0);
276         else {
277             error(0, errno, "lstat: %s", pathname);
278             return(PS_IOERR);
279         }
280     }
281
282     if ((statbuf.st_mode & S_IFLNK) == S_IFLNK) {
283         fprintf(stderr, "File %s must not be a symbolic link.\n", pathname);
284         return(PS_AUTHFAIL);
285     }
286
287     if (statbuf.st_mode & ~(S_IFREG | S_IREAD | S_IWRITE)) {
288         fprintf(stderr, "File %s must have no more than -rw------ (0600) permissions.\n", 
289                 pathname);
290         return(PS_AUTHFAIL);
291     }
292
293     if (statbuf.st_uid != getuid()) {
294         fprintf(stderr, "File %s must be owned by you.\n", pathname);
295         return(PS_AUTHFAIL);
296     }
297
298     return(0);
299 }
300
301 int prc_parse_file (pathname, securecheck)
302 /* digest the configuration into a linked list of host records */
303 const char *pathname;           /* pathname for the configuration file */
304 const bool securecheck;         /* check for a secure rc file? */
305 {
306     prc_errflag = 0;
307     querylist = hosttail = (struct query *)NULL;
308
309     errno = 0;
310
311     /* Check that the file is secure */
312     if (securecheck && (prc_errflag = prc_filecheck(pathname)) != 0)
313         return(prc_errflag);
314
315     if (errno == ENOENT)
316         return(0);
317
318     /* Open the configuration and feed it to the lexer. */
319     if ((yyin = fopen(pathname,"r")) == (FILE *)NULL) {
320         error(0, errno, "open: %s", pathname);
321         return(PS_IOERR);
322     }
323
324     yyparse();          /* parse entire file */
325
326     fclose(yyin);
327
328     if (prc_errflag) 
329         return(PS_SYNTAX);
330     else
331         return(0);
332 }
333
334 static int reset_server(char *name, int skip)
335 /* clear the entire global record and initialize it with a new name */
336 {
337     struct query *ctl;
338
339     /* don't allow name collisions, this screws up the data structures */
340     for (ctl = querylist; ctl; ctl = ctl->next)
341         if (strcmp(name, ctl->server.names->id) == 0)
342             return(FALSE);
343
344     memset(&current,'\0',sizeof(current));
345     current.smtp_socket = -1;
346     save_str(&current.server.names, -1, name);
347     current.server.skip = skip;
348     return(TRUE);
349 }
350
351
352 static void user_reset(void)
353 /* clear the global current record (server parameters) used by the parser */
354 {
355     struct hostdata save;
356
357     /*
358      * Purpose of this code is to initialize the new server block, but
359      * preserve whatever server name was previously set.  Also
360      * preserve server options unless the command-line explicitly
361      * overrides them.
362      */
363     save = current.server;
364
365     memset(&current, '\0', sizeof(current));
366     current.smtp_socket = -1;
367
368     current.server = save;
369 }
370
371 struct query *hostalloc(init)
372 /* append a host record to the host list */
373 struct query *init;     /* pointer to block containing initial values */
374 {
375     struct query *node;
376
377     /* allocate new node */
378     node = (struct query *) xmalloc(sizeof(struct query));
379
380     /* initialize it */
381     memcpy(node, init, sizeof(struct query));
382
383     /* append to end of list */
384     if (hosttail != (struct query *) 0)
385         hosttail->next = node;  /* list contains at least one element */
386     else
387         querylist = node;       /* list is empty */
388     hosttail = node;
389     return(node);
390 }
391
392 static void record_current(void)
393 /* register current parameters and append to the host list */
394 {
395 #define FLAG_FORCE(fld) if (cmd_opts.fld) current.fld = cmd_opts.fld
396     FLAG_FORCE(server.protocol);
397     FLAG_FORCE(server.port);
398     FLAG_FORCE(server.interval);
399     FLAG_FORCE(server.preauthenticate);
400     FLAG_FORCE(server.timeout);
401     FLAG_FORCE(server.envelope);
402     FLAG_FORCE(server.skip);
403     FLAG_FORCE(server.dns);
404
405 #ifdef linux
406     FLAG_FORCE(server.interface);
407     FLAG_FORCE(server.monitor);
408     FLAG_FORCE(server.interface_pair);
409 #endif /* linux */
410
411     FLAG_FORCE(remotename);
412     FLAG_FORCE(password);
413     if (cmd_opts.mailboxes)
414         save_str(&current.mailboxes, -1, cmd_opts.mailboxes->id);
415     if (cmd_opts.smtphunt)
416         save_str(&current.smtphunt, -1, cmd_opts.smtphunt->id);
417     FLAG_FORCE(mda);
418     FLAG_FORCE(preconnect);
419
420     FLAG_FORCE(keep);
421     FLAG_FORCE(flush);
422     FLAG_FORCE(fetchall);
423     FLAG_FORCE(rewrite);
424     FLAG_FORCE(forcecr);
425     FLAG_FORCE(stripcr);
426     FLAG_FORCE(pass8bits);
427     FLAG_FORCE(limit);
428     FLAG_FORCE(fetchlimit);
429     FLAG_FORCE(batchlimit);
430
431 #undef FLAG_FORCE
432
433     (void) hostalloc(&current);
434 }
435
436 void optmerge(struct query *h2, struct query *h1)
437 /* merge two options records; empty fields in h2 are filled in from h1 */
438 {
439     append_str_list(&h2->server.localdomains, &h1->server.localdomains);
440     append_str_list(&h2->localnames, &h1->localnames);
441     append_str_list(&h2->mailboxes, &h1->mailboxes);
442     append_str_list(&h2->smtphunt, &h1->smtphunt);
443
444 #define FLAG_MERGE(fld) if (!h2->fld) h2->fld = h1->fld
445     FLAG_MERGE(server.protocol);
446     FLAG_MERGE(server.port);
447     FLAG_MERGE(server.interval);
448     FLAG_MERGE(server.preauthenticate);
449     FLAG_MERGE(server.timeout);
450     FLAG_MERGE(server.envelope);
451     FLAG_MERGE(server.skip);
452     FLAG_MERGE(server.dns);
453
454 #ifdef linux
455     FLAG_MERGE(server.interface);
456     FLAG_MERGE(server.monitor);
457     FLAG_MERGE(server.interface_pair);
458 #endif /* linux */
459
460     FLAG_MERGE(remotename);
461     FLAG_MERGE(password);
462     FLAG_MERGE(mda);
463     FLAG_MERGE(preconnect);
464
465     FLAG_MERGE(keep);
466     FLAG_MERGE(flush);
467     FLAG_MERGE(fetchall);
468     FLAG_MERGE(rewrite);
469     FLAG_MERGE(forcecr);
470     FLAG_MERGE(stripcr);
471     FLAG_MERGE(pass8bits);
472     FLAG_MERGE(limit);
473     FLAG_MERGE(fetchlimit);
474     FLAG_MERGE(batchlimit);
475 #undef FLAG_MERGE
476 }
477
478 /* easier to do this than cope with variations in where the library lives */
479 int yywrap(void) {return 1;}
480
481 /* rcfile_y.y ends here */