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