]> Pileus Git - ~andy/fetchmail/blob - rcfile_y.y
Added idfile.
[~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     {logfile = xstrdup($4);}
91                 | SET IDFILE optmap STRING      {idfile = xstrdup($4);}
92                 | SET DAEMON optmap NUMBER      {poll_interval = $4;}
93                 | SET SYSLOG                    {errors_to_syslog = TRUE;}
94                 | SET INVISIBLE                 {use_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)
323 /* check that a configuration file is secure */
324 const char *pathname;           /* pathname for the configuration file */
325 {
326 #ifndef __EMX__
327     struct stat statbuf;
328
329     errno = 0;
330
331     /* special cases useful for debugging purposes */
332     if (strcmp("/dev/null", pathname) == 0)
333         return(0);
334
335     /* the run control file must have the same uid as the REAL uid of this 
336        process, it must have permissions no greater than 600, and it must not 
337        be a symbolic link.  We check these conditions here. */
338
339     if (lstat(pathname, &statbuf) < 0) {
340         if (errno == ENOENT) 
341             return(0);
342         else {
343             error(0, errno, "lstat: %s", pathname);
344             return(PS_IOERR);
345         }
346     }
347
348     if ((statbuf.st_mode & S_IFLNK) == S_IFLNK) {
349         fprintf(stderr, "File %s must not be a symbolic link.\n", pathname);
350         return(PS_AUTHFAIL);
351     }
352
353     if (statbuf.st_mode & ~(S_IFREG | S_IREAD | S_IWRITE)) {
354         fprintf(stderr, "File %s must have no more than -rw------ (0600) permissions.\n", 
355                 pathname);
356         return(PS_AUTHFAIL);
357     }
358
359     if (statbuf.st_uid != getuid()) {
360         fprintf(stderr, "File %s must be owned by you.\n", pathname);
361         return(PS_AUTHFAIL);
362     }
363 #endif
364     return(0);
365 }
366
367 int prc_parse_file (const char *pathname, const flag securecheck)
368 /* digest the configuration into a linked list of host records */
369 {
370     prc_errflag = 0;
371     querylist = hosttail = (struct query *)NULL;
372
373     errno = 0;
374
375     /* Check that the file is secure */
376     if (securecheck && (prc_errflag = prc_filecheck(pathname)) != 0)
377         return(prc_errflag);
378
379     if (errno == ENOENT)
380         return(0);
381
382     /* Open the configuration and feed it to the lexer. */
383     if ((yyin = fopen(pathname,"r")) == (FILE *)NULL) {
384         error(0, errno, "open: %s", pathname);
385         return(PS_IOERR);
386     }
387
388     yyparse();          /* parse entire file */
389
390     fclose(yyin);
391
392     if (prc_errflag) 
393         return(PS_SYNTAX);
394     else
395         return(0);
396 }
397
398 static void reset_server(char *name, int skip)
399 /* clear the entire global record and initialize it with a new name */
400 {
401     trailer = FALSE;
402     memset(&current,'\0',sizeof(current));
403     current.smtp_socket = -1;
404     current.server.pollname = xstrdup(name);
405     current.server.skip = skip;
406 }
407
408
409 static void user_reset(void)
410 /* clear the global current record (user parameters) used by the parser */
411 {
412     struct hostdata save;
413
414     /*
415      * Purpose of this code is to initialize the new server block, but
416      * preserve whatever server name was previously set.  Also
417      * preserve server options unless the command-line explicitly
418      * overrides them.
419      */
420     save = current.server;
421
422     memset(&current, '\0', sizeof(current));
423     current.smtp_socket = -1;
424
425     current.server = save;
426 }
427
428 struct query *hostalloc(init)
429 /* append a host record to the host list */
430 struct query *init;     /* pointer to block containing initial values */
431 {
432     struct query *node;
433
434     /* allocate new node */
435     node = (struct query *) xmalloc(sizeof(struct query));
436
437     /* initialize it */
438     if (init)
439         memcpy(node, init, sizeof(struct query));
440     else
441         memset(node, '\0', sizeof(struct query));
442
443     /* append to end of list */
444     if (hosttail != (struct query *) 0)
445         hosttail->next = node;  /* list contains at least one element */
446     else
447         querylist = node;       /* list is empty */
448     hosttail = node;
449
450     if (trailer)
451         node->server.lead_server = leadentry;
452     else
453     {
454         node->server.lead_server = NULL;
455         leadentry = &node->server;
456     }
457
458     return(node);
459 }
460
461 static void record_current(void)
462 /* register current parameters and append to the host list */
463 {
464 #define FLAG_FORCE(fld) if (cmd_opts.fld) current.fld = cmd_opts.fld
465     FLAG_FORCE(server.localdomains);
466     FLAG_FORCE(localnames);
467     FLAG_FORCE(mailboxes);
468     FLAG_FORCE(smtphunt);
469
470     FLAG_FORCE(server.via);
471     FLAG_FORCE(server.protocol);
472 #if INET6
473     FLAG_FORCE(server.service);
474     FLAG_FORCE(server.netsec);
475 #else /* INET6 */
476     FLAG_FORCE(server.port);
477 #endif /* INET6 */
478     FLAG_FORCE(server.interval);
479     FLAG_FORCE(server.preauthenticate);
480     FLAG_FORCE(server.timeout);
481     FLAG_FORCE(server.envelope);
482     FLAG_FORCE(server.envskip);
483     FLAG_FORCE(server.qvirtual);
484     FLAG_FORCE(server.skip);
485     FLAG_FORCE(server.dns);
486     FLAG_FORCE(server.uidl);
487
488 #ifdef linux
489     FLAG_FORCE(server.interface);
490     FLAG_FORCE(server.monitor);
491     FLAG_FORCE(server.interface_pair);
492 #endif /* linux */
493
494     FLAG_FORCE(remotename);
495     FLAG_FORCE(password);
496     FLAG_FORCE(mda);
497     FLAG_FORCE(smtpaddress);
498     FLAG_FORCE(antispam);
499     FLAG_FORCE(preconnect);
500     FLAG_FORCE(postconnect);
501
502     FLAG_FORCE(keep);
503     FLAG_FORCE(flush);
504     FLAG_FORCE(fetchall);
505     FLAG_FORCE(rewrite);
506     FLAG_FORCE(forcecr);
507     FLAG_FORCE(stripcr);
508     FLAG_FORCE(pass8bits);
509     FLAG_FORCE(dropstatus);
510     FLAG_FORCE(mimedecode);
511     FLAG_FORCE(limit);
512     FLAG_FORCE(fetchlimit);
513     FLAG_FORCE(batchlimit);
514     FLAG_FORCE(expunge);
515
516 #undef FLAG_FORCE
517
518     (void) hostalloc(&current);
519
520     trailer = TRUE;
521 }
522
523 void optmerge(struct query *h2, struct query *h1)
524 /* merge two options records; empty fields in h2 are filled in from h1 */
525 {
526     append_str_list(&h2->server.localdomains, &h1->server.localdomains);
527     append_str_list(&h2->localnames, &h1->localnames);
528     append_str_list(&h2->mailboxes, &h1->mailboxes);
529     append_str_list(&h2->smtphunt, &h1->smtphunt);
530
531 #define FLAG_MERGE(fld) if (!h2->fld) h2->fld = h1->fld
532     FLAG_MERGE(server.via);
533     FLAG_MERGE(server.protocol);
534 #if INET6
535     FLAG_MERGE(server.service);
536     FLAG_MERGE(server.netsec);
537 #else /* INET6 */
538     FLAG_MERGE(server.port);
539 #endif /* INET6 */
540     FLAG_MERGE(server.interval);
541     FLAG_MERGE(server.preauthenticate);
542     FLAG_MERGE(server.timeout);
543     FLAG_MERGE(server.envelope);
544     FLAG_MERGE(server.envskip);
545     FLAG_MERGE(server.qvirtual);
546     FLAG_MERGE(server.skip);
547     FLAG_MERGE(server.dns);
548     FLAG_MERGE(server.uidl);
549
550 #ifdef linux
551     FLAG_MERGE(server.interface);
552     FLAG_MERGE(server.monitor);
553     FLAG_MERGE(server.interface_pair);
554 #endif /* linux */
555
556     FLAG_MERGE(remotename);
557     FLAG_MERGE(password);
558     FLAG_MERGE(mda);
559     FLAG_MERGE(smtpaddress);
560     FLAG_MERGE(antispam);
561     FLAG_MERGE(preconnect);
562
563     FLAG_MERGE(keep);
564     FLAG_MERGE(flush);
565     FLAG_MERGE(fetchall);
566     FLAG_MERGE(rewrite);
567     FLAG_MERGE(forcecr);
568     FLAG_MERGE(stripcr);
569     FLAG_MERGE(pass8bits);
570     FLAG_MERGE(dropstatus);
571     FLAG_MERGE(mimedecode);
572     FLAG_MERGE(limit);
573     FLAG_MERGE(fetchlimit);
574     FLAG_MERGE(batchlimit);
575     FLAG_MERGE(expunge);
576 #undef FLAG_MERGE
577 }
578
579 /* easier to do this than cope with variations in where the library lives */
580 int yywrap(void) {return 1;}
581
582 /* rcfile_y.y ends here */
583
584