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