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