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