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