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