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