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