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