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