]> Pileus Git - ~andy/fetchmail/blob - rcfile_y.y
daff1a344cc190b1a3fdec0ea027e4955e5c58fa
[~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 defined(__CYGWIN__)
26 #include <sys/cygwin.h>
27 #endif /* __CYGWIN__ */
28
29 #include "fetchmail.h"
30 #include "i18n.h"
31   
32 /* parser reads these */
33 char *rcfile;                   /* path name of rc file */
34 struct query cmd_opts;          /* where to put command-line info */
35
36 /* parser sets these */
37 struct query *querylist;        /* head of server list (globally visible) */
38
39 int yydebug;                    /* in case we didn't generate with -- debug */
40
41 static struct query current;    /* current server record */
42 static int prc_errflag;
43 static struct hostdata *leadentry;
44 static flag trailer;
45
46 static void record_current(void);
47 static void user_reset(void);
48 static void reset_server(const char *name, int skip);
49
50 /* these should be of size PATH_MAX */
51 char currentwd[1024] = "", rcfiledir[1024] = "";
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 SDPS ENVELOPE QVIRTUAL
66 %token USERNAME PASSWORD FOLDER SMTPHOST FETCHDOMAINS MDA BSMTP LMTP
67 %token SMTPADDRESS SMTPNAME SPAMRESPONSE PRECONNECT POSTCONNECT LIMIT WARNINGS
68 %token INTERFACE MONITOR PLUGIN PLUGOUT
69 %token IS HERE THERE TO MAP WILDCARD
70 %token BATCHLIMIT FETCHLIMIT FETCHSIZELIMIT FASTUIDL EXPUNGE PROPERTIES
71 %token SET LOGFILE DAEMON SYSLOG IDFILE INVISIBLE POSTMASTER BOUNCEMAIL 
72 %token SPAMBOUNCE SHOWDOTS
73 %token <proto> PROTO AUTHTYPE
74 %token <sval>  STRING
75 %token <number> NUMBER
76 %token NO KEEP FLUSH LIMITFLUSH FETCHALL REWRITE FORCECR STRIPCR PASS8BITS 
77 %token DROPSTATUS DROPDELIVERED
78 %token DNS SERVICE PORT UIDL INTERVAL MIMEDECODE IDLE CHECKALIAS 
79 %token SSL SSLKEY SSLCERT SSLPROTO SSLCERTCK SSLCERTPATH SSLFINGERPRINT
80 %token PRINCIPAL ESMTPNAME ESMTPPASSWORD
81 %token TRACEPOLLS
82
83 %%
84
85 rcfile          : /* empty */
86                 | statement_list
87                 ;
88
89 statement_list  : statement
90                 | statement_list statement
91                 ;
92
93 optmap          : MAP | /* EMPTY */;
94
95 /* future global options should also have the form SET <name> optmap <value> */
96 statement       : SET LOGFILE optmap STRING     {run.logfile = prependdir ($4, rcfiledir);}
97                 | SET IDFILE optmap STRING      {run.idfile = prependdir ($4, rcfiledir);}
98                 | SET DAEMON optmap NUMBER      {run.poll_interval = $4;}
99                 | SET POSTMASTER optmap STRING  {run.postmaster = xstrdup($4);}
100                 | SET BOUNCEMAIL                {run.bouncemail = TRUE;}
101                 | SET NO BOUNCEMAIL             {run.bouncemail = FALSE;}
102                 | SET SPAMBOUNCE                {run.spambounce = TRUE;}
103                 | SET NO SPAMBOUNCE             {run.spambounce = FALSE;}
104                 | SET PROPERTIES optmap STRING  {run.properties =xstrdup($4);}
105                 | SET SYSLOG                    {run.use_syslog = TRUE;}
106                 | SET NO SYSLOG                 {run.use_syslog = FALSE;}
107                 | SET INVISIBLE                 {run.invisible = TRUE;}
108                 | SET NO INVISIBLE              {run.invisible = FALSE;}
109                 | SET SHOWDOTS                  {run.showdots = FLAG_TRUE;}
110                 | SET NO SHOWDOTS               {run.showdots = FLAG_FALSE;}
111
112 /* 
113  * The way the next two productions are written depends on the fact that
114  * userspecs cannot be empty.  It's a kluge to deal with files that set
115  * up a load of defaults and then have poll statements following with no
116  * user options at all. 
117  */
118                 | define_server serverspecs             {record_current();}
119                 | define_server serverspecs userspecs
120
121 /* detect and complain about the most common user error */
122                 | define_server serverspecs userspecs serv_option
123                         {yyerror(GT_("server option after user options"));}
124                 ;
125
126 define_server   : POLL STRING           {reset_server($2, FALSE);}
127                 | SKIP STRING           {reset_server($2, TRUE);}
128                 | DEFAULTS              {reset_server("defaults", FALSE);}
129                 ;
130
131 serverspecs     : /* EMPTY */
132                 | serverspecs serv_option
133                 ;
134
135 alias_list      : STRING                {save_str(&current.server.akalist,$1,0);}
136                 | alias_list STRING     {save_str(&current.server.akalist,$2,0);}
137                 ;
138
139 domain_list     : STRING                {save_str(&current.server.localdomains,$1,0);}
140                 | domain_list STRING    {save_str(&current.server.localdomains,$2,0);}
141                 ;
142
143 serv_option     : AKA alias_list
144                 | VIA STRING            {current.server.via = xstrdup($2);}
145                 | LOCALDOMAINS domain_list
146                 | PROTOCOL PROTO        {current.server.protocol = $2;}
147                 | PROTOCOL KPOP         {
148                                             current.server.protocol = P_POP3;
149
150                                             if (current.server.authenticate == A_PASSWORD)
151 #ifdef KERBEROS_V5
152                                                 current.server.authenticate = A_KERBEROS_V5;
153 #else
154                                                 current.server.authenticate = A_KERBEROS_V4;
155 #endif /* KERBEROS_V5 */
156                                             current.server.service = KPOP_PORT;
157                                         }
158                 | PRINCIPAL STRING      {current.server.principal = xstrdup($2);}
159                 | ESMTPNAME STRING      {current.server.esmtp_name = xstrdup($2);}
160                 | ESMTPPASSWORD STRING  {current.server.esmtp_password = xstrdup($2);}
161                 | PROTOCOL SDPS         {
162 #ifdef SDPS_ENABLE
163                                             current.server.protocol = P_POP3;
164                                             current.server.sdps = TRUE;
165 #else
166                                             yyerror(GT_("SDPS not enabled."));
167 #endif /* SDPS_ENABLE */
168                                         }
169                 | UIDL                  {current.server.uidl = FLAG_TRUE;}
170                 | NO UIDL               {current.server.uidl  = FLAG_FALSE;}
171                 | CHECKALIAS            {current.server.checkalias = FLAG_TRUE;}
172                 | NO CHECKALIAS         {current.server.checkalias  = FLAG_FALSE;}
173                 | SERVICE STRING        {
174                                         current.server.service = $2;
175                                         }
176                 | PORT NUMBER           {
177                                         int port = $2;
178                                         char buf[10];
179                                         snprintf(buf, sizeof buf, "%d", port);
180                                         current.server.service = xstrdup(buf);
181                 }
182                 | INTERVAL NUMBER
183                         {current.server.interval = $2;}
184                 | AUTHENTICATE AUTHTYPE
185                         {current.server.authenticate = $2;}
186                 | TIMEOUT NUMBER
187                         {current.server.timeout = $2;}
188                 | ENVELOPE NUMBER STRING 
189                                         {
190                                             current.server.envelope = 
191                                                 xstrdup($3);
192                                             current.server.envskip = $2;
193                                         }
194                 | ENVELOPE STRING
195                                         {
196                                             current.server.envelope = 
197                                                 xstrdup($2);
198                                             current.server.envskip = 0;
199                                         }
200
201                 | QVIRTUAL STRING       {current.server.qvirtual=xstrdup($2);}
202                 | INTERFACE STRING      {
203 #ifdef CAN_MONITOR
204                                         interface_parse($2, &current.server);
205 #else
206                                         fprintf(stderr, GT_("fetchmail: interface option is only supported under Linux (without IPv6) and FreeBSD\n"));
207 #endif
208                                         }
209                 | MONITOR STRING        {
210 #ifdef CAN_MONITOR
211                                         current.server.monitor = xstrdup($2);
212 #else
213                                         fprintf(stderr, GT_("fetchmail: monitor option is only supported under Linux (without IPv6) and FreeBSD\n"));
214 #endif
215                                         }
216                 | PLUGIN STRING         { current.server.plugin = xstrdup($2); }
217                 | PLUGOUT STRING        { current.server.plugout = xstrdup($2); }
218                 | DNS                   {current.server.dns = FLAG_TRUE;}
219                 | NO DNS                {current.server.dns = FLAG_FALSE;}
220                 | NO ENVELOPE           {current.server.envelope = STRING_DISABLED;}
221                 | TRACEPOLLS            {current.server.tracepolls = FLAG_TRUE;}
222                 | NO TRACEPOLLS         {current.server.tracepolls = FLAG_FALSE;}
223                 ;
224
225 userspecs       : user1opts             {record_current(); user_reset();}
226                 | explicits
227                 ;
228
229 explicits       : explicitdef           {record_current(); user_reset();}
230                 | explicits explicitdef {record_current(); user_reset();}
231                 ;
232
233 explicitdef     : userdef user0opts
234                 ;
235
236 userdef         : USERNAME STRING       {current.remotename = xstrdup($2);}
237                 | USERNAME mapping_list HERE
238                 | USERNAME STRING THERE {current.remotename = xstrdup($2);}
239                 ;
240
241 user0opts       : /* EMPTY */
242                 | user0opts user_option
243                 ;
244
245 user1opts       : user_option
246                 | user1opts user_option
247                 ;
248
249 localnames      : WILDCARD              {current.wildcard =  TRUE;}
250                 | mapping_list          {current.wildcard =  FALSE;}
251                 | mapping_list WILDCARD {current.wildcard =  TRUE;}
252                 ;
253
254 mapping_list    : mapping               
255                 | mapping_list mapping
256                 ;
257
258 mapping         : STRING        
259                                 {save_str_pair(&current.localnames, $1, NULL);}
260                 | STRING MAP STRING
261                                 {save_str_pair(&current.localnames, $1, $3);}
262                 ;
263
264 folder_list     : STRING                {save_str(&current.mailboxes,$1,0);}
265                 | folder_list STRING    {save_str(&current.mailboxes,$2,0);}
266                 ;
267
268 smtp_list       : STRING                {save_str(&current.smtphunt, $1,TRUE);}
269                 | smtp_list STRING      {save_str(&current.smtphunt, $2,TRUE);}
270                 ;
271
272 fetch_list      : STRING                {save_str(&current.domainlist, $1,TRUE);}
273                 | fetch_list STRING     {save_str(&current.domainlist, $2,TRUE);}
274                 ;
275
276 num_list        : NUMBER
277                         {
278                             struct idlist *id;
279                             id=save_str(&current.antispam,STRING_DUMMY,0);
280                             id->val.status.num = $1;
281                         }
282                 | num_list NUMBER
283                         {
284                             struct idlist *id;
285                             id=save_str(&current.antispam,STRING_DUMMY,0);
286                             id->val.status.num = $2;
287                         }
288                 ;
289
290 user_option     : TO localnames HERE
291                 | TO localnames
292                 | IS localnames HERE
293                 | IS localnames
294
295                 | IS STRING THERE       {current.remotename  = xstrdup($2);}
296                 | PASSWORD STRING       {current.password    = xstrdup($2);}
297                 | FOLDER folder_list
298                 | SMTPHOST smtp_list
299                 | FETCHDOMAINS fetch_list
300                 | SMTPADDRESS STRING    {current.smtpaddress = xstrdup($2);}
301                 | SMTPNAME STRING       {current.smtpname = xstrdup($2);}
302                 | SPAMRESPONSE num_list
303                 | MDA STRING            {current.mda         = xstrdup($2);}
304                 | BSMTP STRING          {current.bsmtp       = prependdir ($2, rcfiledir);}
305                 | LMTP                  {current.listener    = LMTP_MODE;}
306                 | PRECONNECT STRING     {current.preconnect  = xstrdup($2);}
307                 | POSTCONNECT STRING    {current.postconnect = xstrdup($2);}
308
309                 | KEEP                  {current.keep        = FLAG_TRUE;}
310                 | FLUSH                 {current.flush       = FLAG_TRUE;}
311                 | LIMITFLUSH            {current.limitflush  = FLAG_TRUE;}
312                 | FETCHALL              {current.fetchall    = FLAG_TRUE;}
313                 | REWRITE               {current.rewrite     = FLAG_TRUE;}
314                 | FORCECR               {current.forcecr     = FLAG_TRUE;}
315                 | STRIPCR               {current.stripcr     = FLAG_TRUE;}
316                 | PASS8BITS             {current.pass8bits   = FLAG_TRUE;}
317                 | DROPSTATUS            {current.dropstatus  = FLAG_TRUE;}
318                 | DROPDELIVERED         {current.dropdelivered = FLAG_TRUE;}
319                 | MIMEDECODE            {current.mimedecode  = FLAG_TRUE;}
320                 | IDLE                  {current.idle        = FLAG_TRUE;}
321
322                 | SSL                   {
323 #ifdef SSL_ENABLE
324                     current.use_ssl = FLAG_TRUE;
325 #else
326                     yyerror(GT_("SSL is not enabled"));
327 #endif 
328                 }
329                 | SSLKEY STRING         {current.sslkey = prependdir ($2, rcfiledir);}
330                 | SSLCERT STRING        {current.sslcert = prependdir ($2, rcfiledir);}
331                 | SSLPROTO STRING       {current.sslproto = xstrdup($2);}
332                 | SSLCERTCK             {current.sslcertck = FLAG_TRUE;}
333                 | SSLCERTPATH STRING    {current.sslcertpath = prependdir($2, rcfiledir);}
334                 | SSLFINGERPRINT STRING {current.sslfingerprint = xstrdup($2);}
335
336                 | NO KEEP               {current.keep        = FLAG_FALSE;}
337                 | NO FLUSH              {current.flush       = FLAG_FALSE;}
338                 | NO LIMITFLUSH         {current.limitflush  = FLAG_FALSE;}
339                 | NO FETCHALL           {current.fetchall    = FLAG_FALSE;}
340                 | NO REWRITE            {current.rewrite     = FLAG_FALSE;}
341                 | NO FORCECR            {current.forcecr     = FLAG_FALSE;}
342                 | NO STRIPCR            {current.stripcr     = FLAG_FALSE;}
343                 | NO PASS8BITS          {current.pass8bits   = FLAG_FALSE;}
344                 | NO DROPSTATUS         {current.dropstatus  = FLAG_FALSE;}
345                 | NO DROPDELIVERED      {current.dropdelivered = FLAG_FALSE;}
346                 | NO MIMEDECODE         {current.mimedecode  = FLAG_FALSE;}
347                 | NO IDLE               {current.idle        = FLAG_FALSE;}
348
349                 | NO SSL                {current.use_ssl     = FLAG_FALSE;}
350
351                 | LIMIT NUMBER          {current.limit       = NUM_VALUE_IN($2);}
352                 | WARNINGS NUMBER       {current.warnings    = NUM_VALUE_IN($2);}
353                 | FETCHLIMIT NUMBER     {current.fetchlimit  = NUM_VALUE_IN($2);}
354                 | FETCHSIZELIMIT NUMBER {current.fetchsizelimit = NUM_VALUE_IN($2);}
355                 | FASTUIDL NUMBER       {current.fastuidl    = NUM_VALUE_IN($2);}
356                 | BATCHLIMIT NUMBER     {current.batchlimit  = NUM_VALUE_IN($2);}
357                 | EXPUNGE NUMBER        {current.expunge     = NUM_VALUE_IN($2);}
358
359                 | PROPERTIES STRING     {current.properties  = xstrdup($2);}
360                 ;
361 %%
362
363 /* lexer interface */
364 extern char *rcfile;
365 extern int prc_lineno;
366 extern char *yytext;
367 extern FILE *yyin;
368
369 static struct query *hosttail;  /* where to add new elements */
370
371 void yyerror (const char *s)
372 /* report a syntax error */
373 {
374     report_at_line(stderr, 0, rcfile, prc_lineno, GT_("%s at %s"), s, 
375                    (yytext && yytext[0]) ? yytext : GT_("end of input"));
376     prc_errflag++;
377 }
378
379 int prc_filecheck(const char *pathname, const flag securecheck)
380 /* check that a configuration file is secure */
381 {
382 #ifndef __EMX__
383     struct stat statbuf;
384
385     errno = 0;
386
387     /* special case useful for debugging purposes */
388     if (strcmp("/dev/null", pathname) == 0)
389         return(PS_SUCCESS);
390
391     /* pass through the special name for stdin */
392     if (strcmp("-", pathname) == 0)
393         return(PS_SUCCESS);
394
395     /* the run control file must have the same uid as the REAL uid of this 
396        process, it must have permissions no greater than 600, and it must not 
397        be a symbolic link.  We check these conditions here. */
398
399     if (lstat(pathname, &statbuf) < 0) {
400         if (errno == ENOENT) 
401             return(PS_SUCCESS);
402         else {
403             report(stderr, "lstat: %s: %s\n", pathname, strerror(errno));
404             return(PS_IOERR);
405         }
406     }
407
408     if (!securecheck)   return PS_SUCCESS;
409
410     if (!S_ISREG(statbuf.st_mode))
411     {
412         fprintf(stderr, GT_("File %s must be a regular file.\n"), pathname);
413         return(PS_IOERR);
414     }
415
416 #ifndef __BEOS__
417 #ifdef __CYGWIN__
418     if (cygwin_internal(CW_CHECK_NTSEC, pathname))
419 #endif /* __CYGWIN__ */
420     if (statbuf.st_mode & (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH | S_IXOTH))
421     {
422         fprintf(stderr, GT_("File %s must have no more than -rwx--x--- (0710) permissions.\n"), 
423                 pathname);
424         return(PS_IOERR);
425     }
426 #endif /* __BEOS__ */
427
428 #ifdef HAVE_GETEUID
429     if (statbuf.st_uid != geteuid())
430 #else
431     if (statbuf.st_uid != getuid())
432 #endif /* HAVE_GETEUID */
433     {
434         fprintf(stderr, GT_("File %s must be owned by you.\n"), pathname);
435         return(PS_IOERR);
436     }
437 #endif
438     return(PS_SUCCESS);
439 }
440
441 int prc_parse_file (const char *pathname, const flag securecheck)
442 /* digest the configuration into a linked list of host records */
443 {
444     prc_errflag = 0;
445     querylist = hosttail = (struct query *)NULL;
446
447     errno = 0;
448
449     /* Check that the file is secure */
450     if ( (prc_errflag = prc_filecheck(pathname, securecheck)) != 0 )
451         return(prc_errflag);
452
453     /*
454      * Croak if the configuration directory does not exist.
455      * This probably means an NFS mount failed and we can't
456      * see a configuration file that ought to be there.
457      * Question: is this a portable check? It's not clear
458      * that all implementations of lstat() will return ENOTDIR
459      * rather than plain ENOENT in this case...
460      */
461     if (errno == ENOTDIR)
462         return(PS_IOERR);
463     else if (errno == ENOENT)
464         return(PS_SUCCESS);
465
466     /* Open the configuration file and feed it to the lexer. */
467     if (strcmp(pathname, "-") == 0)
468         yyin = stdin;
469     else if ((yyin = fopen(pathname,"r")) == (FILE *)NULL) {
470         report(stderr, "open: %s: %s\n", pathname, strerror(errno));
471         return(PS_IOERR);
472     }
473
474     yyparse();          /* parse entire file */
475
476     fclose(yyin);       /* not checking this should be safe, file mode was r */
477
478     if (prc_errflag) 
479         return(PS_SYNTAX);
480     else
481         return(PS_SUCCESS);
482 }
483
484 static void reset_server(const char *name, int skip)
485 /* clear the entire global record and initialize it with a new name */
486 {
487     trailer = FALSE;
488     memset(&current,'\0',sizeof(current));
489     current.smtp_socket = -1;
490     current.server.pollname = xstrdup(name);
491     current.server.skip = skip;
492     current.server.principal = (char *)NULL;
493 }
494
495
496 static void user_reset(void)
497 /* clear the global current record (user parameters) used by the parser */
498 {
499     struct hostdata save;
500
501     /*
502      * Purpose of this code is to initialize the new server block, but
503      * preserve whatever server name was previously set.  Also
504      * preserve server options unless the command-line explicitly
505      * overrides them.
506      */
507     save = current.server;
508
509     memset(&current, '\0', sizeof(current));
510     current.smtp_socket = -1;
511
512     current.server = save;
513 }
514
515 struct query *hostalloc(init)
516 /* append a host record to the host list */
517 struct query *init;     /* pointer to block containing initial values */
518 {
519     struct query *node;
520
521     /* allocate new node */
522     node = (struct query *) xmalloc(sizeof(struct query));
523
524     /* initialize it */
525     if (init)
526         memcpy(node, init, sizeof(struct query));
527     else
528     {
529         memset(node, '\0', sizeof(struct query));
530         node->smtp_socket = -1;
531     }
532
533     /* append to end of list */
534     if (hosttail != (struct query *) 0)
535         hosttail->next = node;  /* list contains at least one element */
536     else
537         querylist = node;       /* list is empty */
538     hosttail = node;
539
540     if (trailer)
541         node->server.lead_server = leadentry;
542     else
543     {
544         node->server.lead_server = NULL;
545         leadentry = &node->server;
546     }
547
548     return(node);
549 }
550
551 static void record_current(void)
552 /* register current parameters and append to the host list */
553 {
554     (void) hostalloc(&current);
555     trailer = TRUE;
556 }
557
558 char *prependdir (const char *file, const char *dir)
559 /* if a filename is relative to dir, convert it to an absolute path */
560 {
561     char *newfile;
562     if (!file[0] ||                     /* null path */
563         file[0] == '/' ||               /* absolute path */
564         strcmp(file, "-") == 0 ||       /* stdin/stdout */
565         !dir[0])                        /* we don't HAVE_GETCWD */
566         return xstrdup (file);
567     newfile = xmalloc (strlen (dir) + 1 + strlen (file) + 1);
568     if (dir[strlen(dir) - 1] != '/')
569         sprintf (newfile, "%s/%s", dir, file);
570     else
571         sprintf (newfile, "%s%s", dir, file);
572     return newfile;
573 }
574
575 /* easier to do this than cope with variations in where the library lives */
576 int yywrap(void) {return 1;}
577
578 /* rcfile_y.y ends here */