]> Pileus Git - ~andy/fetchmail/blob - rcfile_y.y
75d57c27c177b5dcb40c506dd98c5a9460e44256
[~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                 | SERVICE NUMBER        {
177                                         int port = $2;
178                                         char buf[10];
179                                         snprintf(buf, sizeof buf, "%d", port);
180                                         current.server.service = xstrdup(buf);
181                 }
182                 | PORT NUMBER           {
183                                         int port = $2;
184                                         char buf[10];
185                                         snprintf(buf, sizeof buf, "%d", port);
186                                         current.server.service = xstrdup(buf);
187                 }
188                 | INTERVAL NUMBER
189                         {current.server.interval = $2;}
190                 | AUTHENTICATE AUTHTYPE
191                         {current.server.authenticate = $2;}
192                 | TIMEOUT NUMBER
193                         {current.server.timeout = $2;}
194                 | ENVELOPE NUMBER STRING 
195                                         {
196                                             current.server.envelope = 
197                                                 xstrdup($3);
198                                             current.server.envskip = $2;
199                                         }
200                 | ENVELOPE STRING
201                                         {
202                                             current.server.envelope = 
203                                                 xstrdup($2);
204                                             current.server.envskip = 0;
205                                         }
206
207                 | QVIRTUAL STRING       {current.server.qvirtual=xstrdup($2);}
208                 | INTERFACE STRING      {
209 #ifdef CAN_MONITOR
210                                         interface_parse($2, &current.server);
211 #else
212                                         fprintf(stderr, GT_("fetchmail: interface option is only supported under Linux (without IPv6) and FreeBSD\n"));
213 #endif
214                                         }
215                 | MONITOR STRING        {
216 #ifdef CAN_MONITOR
217                                         current.server.monitor = xstrdup($2);
218 #else
219                                         fprintf(stderr, GT_("fetchmail: monitor option is only supported under Linux (without IPv6) and FreeBSD\n"));
220 #endif
221                                         }
222                 | PLUGIN STRING         { current.server.plugin = xstrdup($2); }
223                 | PLUGOUT STRING        { current.server.plugout = xstrdup($2); }
224                 | DNS                   {current.server.dns = FLAG_TRUE;}
225                 | NO DNS                {current.server.dns = FLAG_FALSE;}
226                 | NO ENVELOPE           {current.server.envelope = STRING_DISABLED;}
227                 | TRACEPOLLS            {current.server.tracepolls = FLAG_TRUE;}
228                 | NO TRACEPOLLS         {current.server.tracepolls = FLAG_FALSE;}
229                 ;
230
231 userspecs       : user1opts             {record_current(); user_reset();}
232                 | explicits
233                 ;
234
235 explicits       : explicitdef           {record_current(); user_reset();}
236                 | explicits explicitdef {record_current(); user_reset();}
237                 ;
238
239 explicitdef     : userdef user0opts
240                 ;
241
242 userdef         : USERNAME STRING       {current.remotename = xstrdup($2);}
243                 | USERNAME mapping_list HERE
244                 | USERNAME STRING THERE {current.remotename = xstrdup($2);}
245                 ;
246
247 user0opts       : /* EMPTY */
248                 | user0opts user_option
249                 ;
250
251 user1opts       : user_option
252                 | user1opts user_option
253                 ;
254
255 localnames      : WILDCARD              {current.wildcard =  TRUE;}
256                 | mapping_list          {current.wildcard =  FALSE;}
257                 | mapping_list WILDCARD {current.wildcard =  TRUE;}
258                 ;
259
260 mapping_list    : mapping               
261                 | mapping_list mapping
262                 ;
263
264 mapping         : STRING        
265                                 {save_str_pair(&current.localnames, $1, NULL);}
266                 | STRING MAP STRING
267                                 {save_str_pair(&current.localnames, $1, $3);}
268                 ;
269
270 folder_list     : STRING                {save_str(&current.mailboxes,$1,0);}
271                 | folder_list STRING    {save_str(&current.mailboxes,$2,0);}
272                 ;
273
274 smtp_list       : STRING                {save_str(&current.smtphunt, $1,TRUE);}
275                 | smtp_list STRING      {save_str(&current.smtphunt, $2,TRUE);}
276                 ;
277
278 fetch_list      : STRING                {save_str(&current.domainlist, $1,TRUE);}
279                 | fetch_list STRING     {save_str(&current.domainlist, $2,TRUE);}
280                 ;
281
282 num_list        : NUMBER
283                         {
284                             struct idlist *id;
285                             id=save_str(&current.antispam,STRING_DUMMY,0);
286                             id->val.status.num = $1;
287                         }
288                 | num_list NUMBER
289                         {
290                             struct idlist *id;
291                             id=save_str(&current.antispam,STRING_DUMMY,0);
292                             id->val.status.num = $2;
293                         }
294                 ;
295
296 user_option     : TO localnames HERE
297                 | TO localnames
298                 | IS localnames HERE
299                 | IS localnames
300
301                 | IS STRING THERE       {current.remotename  = xstrdup($2);}
302                 | PASSWORD STRING       {current.password    = xstrdup($2);}
303                 | FOLDER folder_list
304                 | SMTPHOST smtp_list
305                 | FETCHDOMAINS fetch_list
306                 | SMTPADDRESS STRING    {current.smtpaddress = xstrdup($2);}
307                 | SMTPNAME STRING       {current.smtpname = xstrdup($2);}
308                 | SPAMRESPONSE num_list
309                 | MDA STRING            {current.mda         = xstrdup($2);}
310                 | BSMTP STRING          {current.bsmtp       = prependdir ($2, rcfiledir);}
311                 | LMTP                  {current.listener    = LMTP_MODE;}
312                 | PRECONNECT STRING     {current.preconnect  = xstrdup($2);}
313                 | POSTCONNECT STRING    {current.postconnect = xstrdup($2);}
314
315                 | KEEP                  {current.keep        = FLAG_TRUE;}
316                 | FLUSH                 {current.flush       = FLAG_TRUE;}
317                 | LIMITFLUSH            {current.limitflush  = FLAG_TRUE;}
318                 | FETCHALL              {current.fetchall    = FLAG_TRUE;}
319                 | REWRITE               {current.rewrite     = FLAG_TRUE;}
320                 | FORCECR               {current.forcecr     = FLAG_TRUE;}
321                 | STRIPCR               {current.stripcr     = FLAG_TRUE;}
322                 | PASS8BITS             {current.pass8bits   = FLAG_TRUE;}
323                 | DROPSTATUS            {current.dropstatus  = FLAG_TRUE;}
324                 | DROPDELIVERED         {current.dropdelivered = FLAG_TRUE;}
325                 | MIMEDECODE            {current.mimedecode  = FLAG_TRUE;}
326                 | IDLE                  {current.idle        = FLAG_TRUE;}
327
328                 | SSL                   {
329 #ifdef SSL_ENABLE
330                     current.use_ssl = FLAG_TRUE;
331 #else
332                     yyerror(GT_("SSL is not enabled"));
333 #endif 
334                 }
335                 | SSLKEY STRING         {current.sslkey = prependdir ($2, rcfiledir);}
336                 | SSLCERT STRING        {current.sslcert = prependdir ($2, rcfiledir);}
337                 | SSLPROTO STRING       {current.sslproto = xstrdup($2);}
338                 | SSLCERTCK             {current.sslcertck = FLAG_TRUE;}
339                 | SSLCERTPATH STRING    {current.sslcertpath = prependdir($2, rcfiledir);}
340                 | SSLFINGERPRINT STRING {current.sslfingerprint = xstrdup($2);}
341
342                 | NO KEEP               {current.keep        = FLAG_FALSE;}
343                 | NO FLUSH              {current.flush       = FLAG_FALSE;}
344                 | NO LIMITFLUSH         {current.limitflush  = FLAG_FALSE;}
345                 | NO FETCHALL           {current.fetchall    = FLAG_FALSE;}
346                 | NO REWRITE            {current.rewrite     = FLAG_FALSE;}
347                 | NO FORCECR            {current.forcecr     = FLAG_FALSE;}
348                 | NO STRIPCR            {current.stripcr     = FLAG_FALSE;}
349                 | NO PASS8BITS          {current.pass8bits   = FLAG_FALSE;}
350                 | NO DROPSTATUS         {current.dropstatus  = FLAG_FALSE;}
351                 | NO DROPDELIVERED      {current.dropdelivered = FLAG_FALSE;}
352                 | NO MIMEDECODE         {current.mimedecode  = FLAG_FALSE;}
353                 | NO IDLE               {current.idle        = FLAG_FALSE;}
354
355                 | NO SSL                {current.use_ssl     = FLAG_FALSE;}
356
357                 | LIMIT NUMBER          {current.limit       = NUM_VALUE_IN($2);}
358                 | WARNINGS NUMBER       {current.warnings    = NUM_VALUE_IN($2);}
359                 | FETCHLIMIT NUMBER     {current.fetchlimit  = NUM_VALUE_IN($2);}
360                 | FETCHSIZELIMIT NUMBER {current.fetchsizelimit = NUM_VALUE_IN($2);}
361                 | FASTUIDL NUMBER       {current.fastuidl    = NUM_VALUE_IN($2);}
362                 | BATCHLIMIT NUMBER     {current.batchlimit  = NUM_VALUE_IN($2);}
363                 | EXPUNGE NUMBER        {current.expunge     = NUM_VALUE_IN($2);}
364
365                 | PROPERTIES STRING     {current.properties  = xstrdup($2);}
366                 ;
367 %%
368
369 /* lexer interface */
370 extern char *rcfile;
371 extern int prc_lineno;
372 extern char *yytext;
373 extern FILE *yyin;
374
375 static struct query *hosttail;  /* where to add new elements */
376
377 void yyerror (const char *s)
378 /* report a syntax error */
379 {
380     report_at_line(stderr, 0, rcfile, prc_lineno, GT_("%s at %s"), s, 
381                    (yytext && yytext[0]) ? yytext : GT_("end of input"));
382     prc_errflag++;
383 }
384
385 int prc_filecheck(const char *pathname, const flag securecheck)
386 /* check that a configuration file is secure */
387 {
388 #ifndef __EMX__
389     struct stat statbuf;
390
391     errno = 0;
392
393     /* special case useful for debugging purposes */
394     if (strcmp("/dev/null", pathname) == 0)
395         return(PS_SUCCESS);
396
397     /* pass through the special name for stdin */
398     if (strcmp("-", pathname) == 0)
399         return(PS_SUCCESS);
400
401     /* the run control file must have the same uid as the REAL uid of this 
402        process, it must have permissions no greater than 600, and it must not 
403        be a symbolic link.  We check these conditions here. */
404
405     if (lstat(pathname, &statbuf) < 0) {
406         if (errno == ENOENT) 
407             return(PS_SUCCESS);
408         else {
409             report(stderr, "lstat: %s: %s\n", pathname, strerror(errno));
410             return(PS_IOERR);
411         }
412     }
413
414     if (!securecheck)   return PS_SUCCESS;
415
416     if (!S_ISREG(statbuf.st_mode))
417     {
418         fprintf(stderr, GT_("File %s must be a regular file.\n"), pathname);
419         return(PS_IOERR);
420     }
421
422 #ifndef __BEOS__
423 #ifdef __CYGWIN__
424     if (cygwin_internal(CW_CHECK_NTSEC, pathname))
425 #endif /* __CYGWIN__ */
426     if (statbuf.st_mode & (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH | S_IXOTH))
427     {
428         fprintf(stderr, GT_("File %s must have no more than -rwx--x--- (0710) permissions.\n"), 
429                 pathname);
430         return(PS_IOERR);
431     }
432 #endif /* __BEOS__ */
433
434 #ifdef HAVE_GETEUID
435     if (statbuf.st_uid != geteuid())
436 #else
437     if (statbuf.st_uid != getuid())
438 #endif /* HAVE_GETEUID */
439     {
440         fprintf(stderr, GT_("File %s must be owned by you.\n"), pathname);
441         return(PS_IOERR);
442     }
443 #endif
444     return(PS_SUCCESS);
445 }
446
447 int prc_parse_file (const char *pathname, const flag securecheck)
448 /* digest the configuration into a linked list of host records */
449 {
450     prc_errflag = 0;
451     querylist = hosttail = (struct query *)NULL;
452
453     errno = 0;
454
455     /* Check that the file is secure */
456     if ( (prc_errflag = prc_filecheck(pathname, securecheck)) != 0 )
457         return(prc_errflag);
458
459     /*
460      * Croak if the configuration directory does not exist.
461      * This probably means an NFS mount failed and we can't
462      * see a configuration file that ought to be there.
463      * Question: is this a portable check? It's not clear
464      * that all implementations of lstat() will return ENOTDIR
465      * rather than plain ENOENT in this case...
466      */
467     if (errno == ENOTDIR)
468         return(PS_IOERR);
469     else if (errno == ENOENT)
470         return(PS_SUCCESS);
471
472     /* Open the configuration file and feed it to the lexer. */
473     if (strcmp(pathname, "-") == 0)
474         yyin = stdin;
475     else if ((yyin = fopen(pathname,"r")) == (FILE *)NULL) {
476         report(stderr, "open: %s: %s\n", pathname, strerror(errno));
477         return(PS_IOERR);
478     }
479
480     yyparse();          /* parse entire file */
481
482     fclose(yyin);       /* not checking this should be safe, file mode was r */
483
484     if (prc_errflag) 
485         return(PS_SYNTAX);
486     else
487         return(PS_SUCCESS);
488 }
489
490 static void reset_server(const char *name, int skip)
491 /* clear the entire global record and initialize it with a new name */
492 {
493     trailer = FALSE;
494     memset(&current,'\0',sizeof(current));
495     current.smtp_socket = -1;
496     current.server.pollname = xstrdup(name);
497     current.server.skip = skip;
498     current.server.principal = (char *)NULL;
499 }
500
501
502 static void user_reset(void)
503 /* clear the global current record (user parameters) used by the parser */
504 {
505     struct hostdata save;
506
507     /*
508      * Purpose of this code is to initialize the new server block, but
509      * preserve whatever server name was previously set.  Also
510      * preserve server options unless the command-line explicitly
511      * overrides them.
512      */
513     save = current.server;
514
515     memset(&current, '\0', sizeof(current));
516     current.smtp_socket = -1;
517
518     current.server = save;
519 }
520
521 /** append a host record to the host list */
522 struct query *hostalloc(struct query *init /** pointer to block containing
523                                                initial values */)
524 {
525     struct query *node;
526
527     /* allocate new node */
528     node = (struct query *) xmalloc(sizeof(struct query));
529
530     /* initialize it */
531     if (init)
532         memcpy(node, init, sizeof(struct query));
533     else
534     {
535         memset(node, '\0', sizeof(struct query));
536         node->smtp_socket = -1;
537     }
538
539     /* append to end of list */
540     if (hosttail != (struct query *) 0)
541         hosttail->next = node;  /* list contains at least one element */
542     else
543         querylist = node;       /* list is empty */
544     hosttail = node;
545
546     if (trailer)
547         node->server.lead_server = leadentry;
548     else
549     {
550         node->server.lead_server = NULL;
551         leadentry = &node->server;
552     }
553
554     return(node);
555 }
556
557 static void record_current(void)
558 /* register current parameters and append to the host list */
559 {
560     (void) hostalloc(&current);
561     trailer = TRUE;
562 }
563
564 char *prependdir (const char *file, const char *dir)
565 /* if a filename is relative to dir, convert it to an absolute path */
566 {
567     char *newfile;
568     if (!file[0] ||                     /* null path */
569         file[0] == '/' ||               /* absolute path */
570         strcmp(file, "-") == 0 ||       /* stdin/stdout */
571         !dir[0])                        /* we don't HAVE_GETCWD */
572         return xstrdup (file);
573     newfile = xmalloc (strlen (dir) + 1 + strlen (file) + 1);
574     if (dir[strlen(dir) - 1] != '/')
575         sprintf (newfile, "%s/%s", dir, file);
576     else
577         sprintf (newfile, "%s%s", dir, file);
578     return newfile;
579 }
580
581 /* easier to do this than cope with variations in where the library lives */
582 int yywrap(void) {return 1;}
583
584 /* rcfile_y.y ends here */