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