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