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