]> Pileus Git - ~andy/fetchmail/blob - rcfile_y.y
7935ba1779da4cbaef7202d59ff1afe09a670df4
[~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 AUTHENTICATE 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                 | AUTHENTICATE PASSWORD {current.server.preauthenticate = A_PASSWORD;}
171                 | AUTHENTICATE KERBEROS4        {current.server.preauthenticate = A_KERBEROS_V4;}
172                 | AUTHENTICATE KERBEROS5        {current.server.preauthenticate = A_KERBEROS_V5;}
173                 | AUTHENTICATE 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($2);}
331                 | WARNINGS NUMBER       {current.warnings    = NUM_VALUE($2);}
332                 | FETCHLIMIT NUMBER     {current.fetchlimit  = NUM_VALUE($2);}
333                 | BATCHLIMIT NUMBER     {current.batchlimit  = NUM_VALUE($2);}
334                 | EXPUNGE NUMBER        {current.expunge     = NUM_VALUE($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_AUTHFAIL);
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_AUTHFAIL);
398     }
399
400     if (statbuf.st_uid != getuid())
401     {
402         fprintf(stderr, "File %s must be owned by you.\n", pathname);
403         return(PS_AUTHFAIL);
404     }
405 #endif
406     return(PS_SUCCESS);
407 }
408
409 int prc_parse_file (const char *pathname, const flag securecheck)
410 /* digest the configuration into a linked list of host records */
411 {
412     prc_errflag = 0;
413     querylist = hosttail = (struct query *)NULL;
414
415     errno = 0;
416
417     /* Check that the file is secure */
418     if ( (prc_errflag = prc_filecheck(pathname, securecheck)) != 0 )
419         return(prc_errflag);
420
421     if (errno == ENOENT)
422         return(PS_SUCCESS);
423
424     /* Open the configuration file and feed it to the lexer. */
425     if (strcmp(pathname, "-") == 0)
426         yyin = stdin;
427     else if ((yyin = fopen(pathname,"r")) == (FILE *)NULL) {
428         report(stderr, "open: %s: %s\n", pathname, strerror(errno));
429         return(PS_IOERR);
430     }
431
432     yyparse();          /* parse entire file */
433
434     fclose(yyin);
435
436     if (prc_errflag) 
437         return(PS_SYNTAX);
438     else
439         return(PS_SUCCESS);
440 }
441
442 static void reset_server(const char *name, int skip)
443 /* clear the entire global record and initialize it with a new name */
444 {
445     trailer = FALSE;
446     memset(&current,'\0',sizeof(current));
447     current.smtp_socket = -1;
448     current.server.pollname = xstrdup(name);
449     current.server.skip = skip;
450 }
451
452
453 static void user_reset(void)
454 /* clear the global current record (user parameters) used by the parser */
455 {
456     struct hostdata save;
457
458     /*
459      * Purpose of this code is to initialize the new server block, but
460      * preserve whatever server name was previously set.  Also
461      * preserve server options unless the command-line explicitly
462      * overrides them.
463      */
464     save = current.server;
465
466     memset(&current, '\0', sizeof(current));
467     current.smtp_socket = -1;
468
469     current.server = save;
470 }
471
472 struct query *hostalloc(init)
473 /* append a host record to the host list */
474 struct query *init;     /* pointer to block containing initial values */
475 {
476     struct query *node;
477
478     /* allocate new node */
479     node = (struct query *) xmalloc(sizeof(struct query));
480
481     /* initialize it */
482     if (init)
483         memcpy(node, init, sizeof(struct query));
484     else
485     {
486         memset(node, '\0', sizeof(struct query));
487         node->smtp_socket = -1;
488     }
489
490     /* append to end of list */
491     if (hosttail != (struct query *) 0)
492         hosttail->next = node;  /* list contains at least one element */
493     else
494         querylist = node;       /* list is empty */
495     hosttail = node;
496
497     if (trailer)
498         node->server.lead_server = leadentry;
499     else
500     {
501         node->server.lead_server = NULL;
502         leadentry = &node->server;
503     }
504
505     return(node);
506 }
507
508 static void record_current(void)
509 /* register current parameters and append to the host list */
510 {
511     (void) hostalloc(&current);
512     trailer = TRUE;
513 }
514
515 /* easier to do this than cope with variations in where the library lives */
516 int yywrap(void) {return 1;}
517
518 /* rcfile_y.y ends here */
519
520