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