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