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