]> Pileus Git - ~andy/fetchmail/blob - rcfile_y.y
Added `logfile' keyword to rc syntax.
[~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 prc_register();
34 static void prc_reset();
35 %}
36
37 %union {
38   int proto;
39   int flag;
40   int number;
41   char *sval;
42 }
43
44 %token DEFAULTS POLL SKIP AKA PROTOCOL AUTHENTICATE TIMEOUT KPOP KERBEROS
45 %token USERNAME PASSWORD FOLDER SMTPHOST MDA IS HERE THERE TO MAP LIMIT
46 %token SET BATCHLIMIT LOGFILE
47 %token <proto> PROTO
48 %token <sval>  STRING
49 %token <number> NUMBER
50 %token <flag>  KEEP FLUSH FETCHALL REWRITE PORT
51
52 /* these are actually used by the lexer */
53 %token FLAG_TRUE        2
54 %token FLAG_FALSE       1
55
56 %%
57
58 rcfile          : /* empty */
59                 | statement_list
60                 ;
61
62 statement_list  : statement
63                 | statement_list statement
64                 ;
65
66 /* future global options should also have the form SET <name> <value> */
67 statement       : SET BATCHLIMIT MAP NUMBER     {batchlimit = $4;}
68                 | SET LOGFILE STRING            {logfile = xstrdup($3);}
69
70 /* 
71  * The way the next two productions are written depends on the fact that
72  * userspecs cannot be empty.  It's a kluge to deal with files that sset
73  * up a load of defaults and then have poll statements following with no
74  * user options at all. 
75  */
76                 | define_server serverspecs     {prc_register(); prc_reset();}
77                 | define_server serverspecs userspecs
78                 ;
79
80 define_server   : POLL STRING   {strcpy(current.servername, $2);}
81                 | SKIP STRING   {strcpy(current.servername, $2);
82                                                 current.skip = TRUE;}
83                 | DEFAULTS      {strcpy(current.servername,"defaults");}
84                 ;
85
86 serverspecs     : /* EMPTY */
87                 | serverspecs serv_option
88                 ;
89
90 alias_list      : STRING                {save_uid(&current.aka, -1, $1);}
91                 | alias_list STRING     {save_uid(&current.aka, -1, $2);}
92                 ;
93
94 serv_option     : AKA alias_list
95                 | PROTOCOL PROTO        {current.protocol = $2;}
96                 | PROTOCOL KPOP         {
97                                             current.protocol = P_POP3;
98                                             current.authenticate = A_KERBEROS;
99                                             current.port = KPOP_PORT;
100                                         }
101                 | PORT NUMBER           {current.port = $2;}
102                 | AUTHENTICATE PASSWORD {current.authenticate = A_PASSWORD;}
103                 | AUTHENTICATE KERBEROS {current.authenticate = A_KERBEROS;}
104                 | TIMEOUT NUMBER        {current.timeout = $2;}
105                 ;
106
107 /*
108  * The first and only the first user spec may omit the USERNAME part.
109  * This is a backward-compatibility kluge to allow old popclient files
110  * to keep working.
111  */
112 userspecs       : user1opts             {prc_register(); prc_reset();}
113                 | user1opts explicits   {prc_register(); prc_reset();}
114                 | explicits
115                 ;
116
117 explicits       : explicitdef           {prc_register(); prc_reset();}
118                 | explicits explicitdef {prc_register(); prc_reset();}
119                 ;
120
121 explicitdef     : userdef user0opts
122                 ;
123
124 userdef         : USERNAME STRING       {strcpy(current.remotename, $2);}
125                 | USERNAME mapping_list HERE
126                 | USERNAME STRING THERE {strcpy(current.remotename, $2);}
127                 ;
128
129 user0opts       : /* EMPTY */
130                 | user0opts user_option
131                 ;
132
133 user1opts       : user_option
134                 | user1opts user_option
135                 ;
136
137 mapping_list    : mapping               
138                 | mapping_list mapping
139                 ;
140
141 mapping         : STRING        
142                                 {save_id_pair(&current.localnames, $1, NULL);}
143                 | STRING MAP STRING
144                                 {save_id_pair(&current.localnames, $1, $3);}
145                 ;
146
147 user_option     : TO mapping_list HERE
148                 | TO mapping_list
149                 | IS mapping_list HERE
150                 | IS mapping_list
151
152                 | IS STRING THERE       {strcpy(current.remotename, $2);}
153                 | PASSWORD STRING       {strcpy(current.password, $2);}
154                 | FOLDER STRING         {strcpy(current.mailbox, $2);}
155                 | SMTPHOST STRING       {strcpy(current.smtphost, $2);}
156                 | MDA STRING            {strcpy(current.mda, $2);}
157
158                 | KEEP                  {current.keep = ($1==FLAG_TRUE);}
159                 | FLUSH                 {current.flush = ($1==FLAG_TRUE);}
160                 | FETCHALL              {current.fetchall = ($1==FLAG_TRUE);}
161                 | REWRITE               {current.norewrite = ($1==FLAG_TRUE);}
162                 | LIMIT NUMBER          {current.limit = $2;}
163                 ;
164 %%
165
166 /* lexer interface */
167 extern char *rcfile;
168 extern int prc_lineno;
169 extern char *yytext;
170 extern FILE *yyin;
171
172 static struct query *hosttail;  /* where to add new elements */
173
174 void yyerror (const char *s)
175 /* report a syntax error */
176 {
177     fprintf(stderr,"%s line %d: %s at %s\n", rcfile, prc_lineno, s, yytext);
178     prc_errflag++;
179 }
180
181 int prc_filecheck(pathname)
182 /* check that a configuration file is secure */
183 const char *pathname;           /* pathname for the configuration file */
184 {
185     struct stat statbuf;
186
187     /* the run control file must have the same uid as the REAL uid of this 
188        process, it must have permissions no greater than 600, and it must not 
189        be a symbolic link.  We check these conditions here. */
190
191     errno = 0;
192     if (lstat(pathname, &statbuf) < 0) {
193         if (errno == ENOENT) 
194             return(0);
195         else {
196             perror(pathname);
197             return(PS_IOERR);
198         }
199     }
200
201     if ((statbuf.st_mode & S_IFLNK) == S_IFLNK) {
202         fprintf(stderr, "File %s must not be a symbolic link.\n", pathname);
203         return(PS_AUTHFAIL);
204     }
205
206     if (statbuf.st_mode & ~(S_IFREG | S_IREAD | S_IWRITE)) {
207         fprintf(stderr, "File %s must have no more than -rw------ permissions.\n", 
208                 pathname);
209         return(PS_AUTHFAIL);
210     }
211
212     if (statbuf.st_uid != getuid()) {
213         fprintf(stderr, "File %s must be owned by you.\n", pathname);
214         return(PS_AUTHFAIL);
215     }
216
217     return(0);
218 }
219
220 int prc_parse_file (pathname)
221 /* digest the configuration into a linked list of host records */
222 const char *pathname;           /* pathname for the configuration file */
223 {
224     prc_errflag = 0;
225     querylist = hosttail = (struct query *)NULL;
226     prc_reset();
227
228     /* Check that the file is secure */
229     if ((prc_errflag = prc_filecheck(pathname)) != 0)
230         return(prc_errflag);
231
232     if (errno == ENOENT)
233         return(0);
234
235     /* Open the configuration and feed it to the lexer. */
236     if ((yyin = fopen(pathname,"r")) == (FILE *)NULL) {
237         perror(pathname);
238         return(PS_IOERR);
239     }
240
241     yyparse();          /* parse entire file */
242
243     fclose(yyin);
244
245     if (prc_errflag) 
246         return(PS_SYNTAX);
247     else
248         return(0);
249 }
250
251 static void prc_reset(void)
252 /* clear the global current record (server parameters) used by the parser */
253 {
254     char        savename[HOSTLEN+1];
255     int         saveport, saveproto, saveauth, saveskip;
256     struct idlist *saveaka;
257
258     /*
259      * Purpose of this code is to initialize the new server block, but
260      * preserve whatever server name was previously set.  Also
261      * preserve server options unless the command-line explicitly
262      * overrides them.
263      */
264     (void) strcpy(savename, current.servername);
265     saveport = current.port;
266     saveproto = current.protocol;
267     saveauth = current.authenticate;
268     saveskip = current.skip;
269     saveaka = current.aka;
270
271     memset(&current, '\0', sizeof(current));
272
273     (void) strcpy(current.servername, savename);
274     current.protocol = saveproto;
275     current.authenticate = saveauth;
276     current.skip = saveskip;
277     current.aka = saveaka;
278 }
279
280 struct query *hostalloc(init)
281 /* append a host record to the host list */
282 struct query *init;     /* pointer to block containing initial values */
283 {
284     struct query *node;
285
286     /* allocate new node */
287     node = (struct query *) xmalloc(sizeof(struct query));
288
289     /* initialize it */
290     memcpy(node, init, sizeof(struct query));
291
292     /* append to end of list */
293     if (hosttail != (struct query *) 0)
294         hosttail->next = node;  /* list contains at least one element */
295     else
296         querylist = node;       /* list is empty */
297     hosttail = node;
298     return(node);
299 }
300
301 static void prc_register(void)
302 /* register current parameters and append to the host list */
303 {
304 #define STR_FORCE(fld, len) if (cmd_opts.fld[0]) \
305                                         strcpy(current.fld, cmd_opts.fld)
306     STR_FORCE(remotename, USERNAMELEN);
307     STR_FORCE(password, PASSWORDLEN);
308     STR_FORCE(mailbox, FOLDERLEN);
309     STR_FORCE(smtphost, HOSTLEN);
310     STR_FORCE(mda, MDALEN);
311 #undef STR_FORCE
312     
313 #define FLAG_FORCE(fld) if (cmd_opts.fld) current.fld = cmd_opts.fld
314     FLAG_FORCE(protocol);
315     FLAG_FORCE(keep);
316     FLAG_FORCE(flush);
317     FLAG_FORCE(fetchall);
318     FLAG_FORCE(norewrite);
319     FLAG_FORCE(skip);
320     FLAG_FORCE(port);
321     FLAG_FORCE(authenticate);
322     FLAG_FORCE(timeout);
323     FLAG_FORCE(limit);
324 #undef FLAG_FORCE
325
326     (void) hostalloc(&current);
327 }
328
329 void optmerge(struct query *h2, struct query *h1)
330 /* merge two options records; empty fields in h2 are filled in from h1 */
331 {
332     append_uid_list(&h2->localnames, &h1->localnames);
333     append_uid_list(&h2->aka, &h1->aka);
334
335 #define STR_MERGE(fld, len) if (*(h2->fld) == '\0') strcpy(h2->fld, h1->fld)
336     STR_MERGE(remotename, USERNAMELEN);
337     STR_MERGE(password, PASSWORDLEN);
338     STR_MERGE(mailbox, FOLDERLEN);
339     STR_MERGE(smtphost, HOSTLEN);
340     STR_MERGE(mda, MDALEN);
341 #undef STR_MERGE
342
343 #define FLAG_MERGE(fld) if (!h2->fld) h2->fld = h1->fld
344     FLAG_MERGE(protocol);
345     FLAG_MERGE(keep);
346     FLAG_MERGE(flush);
347     FLAG_MERGE(fetchall);
348     FLAG_MERGE(norewrite);
349     FLAG_MERGE(skip);
350     FLAG_MERGE(port);
351     FLAG_MERGE(authenticate);
352     FLAG_MERGE(timeout);
353     FLAG_MERGE(limit);
354 #undef FLAG_MERGE
355 }
356
357 /* easier to do this than cope with variations in where the library lives */
358 int yywrap(void) {return 1;}
359
360 /* rcfile_y.y ends here */