]> Pileus Git - ~andy/fetchmail/blob - rcfile_y.y
d56335843fa0c5eebac8c085b6a478525c20c13b
[~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     if (statbuf.st_mode & ~(S_IFREG | S_IREAD | S_IWRITE | S_IEXEC | S_IXGRP))
409     {
410         fprintf(stderr, "File %s must have no more than -rwx--x--- (0710) permissions.\n", 
411                 pathname);
412         return(PS_IOERR);
413     }
414
415 #ifdef HAVE_GETEUID
416     if (statbuf.st_uid != geteuid())
417 #else
418     if (statbuf.st_uid != getuid())
419 #endif /* HAVE_GETEUID */
420     {
421         fprintf(stderr, "File %s must be owned by you.\n", pathname);
422         return(PS_IOERR);
423     }
424 #endif
425     return(PS_SUCCESS);
426 }
427
428 int prc_parse_file (const char *pathname, const flag securecheck)
429 /* digest the configuration into a linked list of host records */
430 {
431     prc_errflag = 0;
432     querylist = hosttail = (struct query *)NULL;
433
434     errno = 0;
435
436     /* Check that the file is secure */
437     if ( (prc_errflag = prc_filecheck(pathname, securecheck)) != 0 )
438         return(prc_errflag);
439
440     /*
441      * Croak if the configuration directory does not exist.
442      * This probably means an NFS mount failed and we can't
443      * see a configuration file that ought to be there.
444      * Question: is this a portable check? It's not clear
445      * that all implementations of lstat() will return ENOTDIR
446      * rather than plain ENOENT in this case...
447      */
448     if (errno == ENOTDIR)
449         return(PS_IOERR);
450     else if (errno == ENOENT)
451         return(PS_SUCCESS);
452
453     /* Open the configuration file and feed it to the lexer. */
454     if (strcmp(pathname, "-") == 0)
455         yyin = stdin;
456     else if ((yyin = fopen(pathname,"r")) == (FILE *)NULL) {
457         report(stderr, "open: %s: %s\n", pathname, strerror(errno));
458         return(PS_IOERR);
459     }
460
461     yyparse();          /* parse entire file */
462
463     fclose(yyin);       /* not checking this should be safe, file mode was r */
464
465     if (prc_errflag) 
466         return(PS_SYNTAX);
467     else
468         return(PS_SUCCESS);
469 }
470
471 static void reset_server(const char *name, int skip)
472 /* clear the entire global record and initialize it with a new name */
473 {
474     trailer = FALSE;
475     memset(&current,'\0',sizeof(current));
476     current.smtp_socket = -1;
477     current.server.pollname = xstrdup(name);
478     current.server.skip = skip;
479 }
480
481
482 static void user_reset(void)
483 /* clear the global current record (user parameters) used by the parser */
484 {
485     struct hostdata save;
486
487     /*
488      * Purpose of this code is to initialize the new server block, but
489      * preserve whatever server name was previously set.  Also
490      * preserve server options unless the command-line explicitly
491      * overrides them.
492      */
493     save = current.server;
494
495     memset(&current, '\0', sizeof(current));
496     current.smtp_socket = -1;
497
498     current.server = save;
499 }
500
501 struct query *hostalloc(init)
502 /* append a host record to the host list */
503 struct query *init;     /* pointer to block containing initial values */
504 {
505     struct query *node;
506
507     /* allocate new node */
508     node = (struct query *) xmalloc(sizeof(struct query));
509
510     /* initialize it */
511     if (init)
512         memcpy(node, init, sizeof(struct query));
513     else
514     {
515         memset(node, '\0', sizeof(struct query));
516         node->smtp_socket = -1;
517     }
518
519     /* append to end of list */
520     if (hosttail != (struct query *) 0)
521         hosttail->next = node;  /* list contains at least one element */
522     else
523         querylist = node;       /* list is empty */
524     hosttail = node;
525
526     if (trailer)
527         node->server.lead_server = leadentry;
528     else
529     {
530         node->server.lead_server = NULL;
531         leadentry = &node->server;
532     }
533
534     return(node);
535 }
536
537 static void record_current(void)
538 /* register current parameters and append to the host list */
539 {
540     (void) hostalloc(&current);
541     trailer = TRUE;
542 }
543
544 /* easier to do this than cope with variations in where the library lives */
545 int yywrap(void) {return 1;}
546
547 /* rcfile_y.y ends here */
548
549