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