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