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