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