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