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