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