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