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