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