]> Pileus Git - ~andy/fetchmail/blob - rcfile_y.y
Simplified rcfile-parsing code.
[~andy/fetchmail] / rcfile_y.y
1 %{
2 /*
3  * For license terms, see the file COPYING in this directory.
4  */
5
6 /***********************************************************************
7   module:       rcfile_y.y
8   project:      fetchmail
9   programmer:   Eric S. Raymond <esr@thyrsus.com>
10   description:  fetchmail configuration file parser
11
12  ***********************************************************************/
13
14 #include <config.h>
15 #include <stdio.h>
16 #include <sys/types.h>
17 #include <sys/file.h>
18 #include <sys/wait.h>
19 #include <sys/stat.h>
20 #include <errno.h>
21 #include "fetchmail.h"
22
23 struct hostrec cmd_opts;        /* where to put command-line info */
24 struct hostrec *hostlist;       /* head of server list (globally visible) */
25
26 int yydebug;    /* in case we didn't generate with -- debug */
27
28 static struct hostrec current;          /* current server record */
29 static int prc_errflag;
30 %}
31
32 %union {
33   int proto;
34   int flag;
35   char *sval;
36 }
37
38 %token DEFAULTS SERVER PROTOCOL AUTHENTICATE KPOP KERBEROS
39 %token USERNAME PASSWORD FOLDER SMTPHOST MDA IS HERE THERE
40 %token <proto> PROTO
41 %token <sval>  STRING
42 %token <flag>  KEEP FLUSH FETCHALL REWRITE PORT SKIP
43
44 /* these are actually used by the lexer */
45 %token FLAG_TRUE        2
46 %token FLAG_FALSE       1
47
48 %%
49
50 rcfile          : /* empty */
51                 | statement_list
52                 ;
53
54 statement_list  : statement
55                 | statement_list statement
56                 ;
57
58 statement       : define_server serverspecs userspecs      
59                 ;
60
61 define_server   : SERVER STRING         {strcpy(current.servername, $2);}
62                 | SKIP SERVER STRING    {strcpy(current.servername, $3);
63                                                 current.skip=($1==FLAG_TRUE);}
64                 | DEFAULTS      {strcpy(current.servername,"defaults");}
65                 ;
66
67 serverspecs     : /* EMPTY */
68                 | serverspecs serv_option
69                 ;
70
71 serv_option     : PROTOCOL PROTO        {current.protocol = $2;}
72                 | PROTOCOL KPOP         {
73                                             current.protocol = P_POP3;
74                                             current.authenticate = A_KERBEROS;
75                                             current.port = KPOP_PORT;
76                                         }
77                 | PORT STRING           {current.port = atoi($2);}
78                 | SKIP                  {current.skip = ($1==FLAG_TRUE);}
79                 | AUTHENTICATE PASSWORD {current.authenticate = A_PASSWORD;}
80                 | AUTHENTICATE KERBEROS {current.authenticate = A_KERBEROS;}
81                 ;
82
83 /* the first and only the first user spec may omit the USERNAME part */
84 userspecs       : user1opts             {prc_register(); prc_reset();}
85                 | user1opts explicits   {prc_register(); prc_reset();}
86                 | explicits
87                 ;
88
89 explicits       : explicitdef           {prc_register(); prc_reset();}
90                 | explicits explicitdef {prc_register(); prc_reset();}
91                 ;
92
93 explicitdef     : userdef user0opts
94                 ;
95
96 userdef         : USERNAME STRING       {strcpy(current.remotename, $2);}
97                 | USERNAME STRING HERE  {strcpy(current.localname, $2);}
98                 | USERNAME STRING THERE {strcpy(current.remotename, $2);}
99                 ;
100
101 user0opts       : /* EMPTY */
102                 | user0opts user_option
103                 ;
104
105 user1opts       : user_option
106                 | user1opts user_option
107                 ;
108
109 user_option     : IS STRING             {strcpy(current.localname, $2);}
110                 | IS STRING HERE        {strcpy(current.localname, $2);}
111                 | IS STRING THERE       {strcpy(current.remotename, $2);}
112                 | PASSWORD STRING       {strcpy(current.password, $2);}
113                 | FOLDER STRING         {strcpy(current.mailbox, $2);}
114                 | SMTPHOST STRING       {strcpy(current.smtphost, $2);}
115                 | MDA STRING            {strcpy(current.mda, $2);}
116
117                 | KEEP                  {current.keep = ($1==FLAG_TRUE);}
118                 | FLUSH                 {current.flush = ($1==FLAG_TRUE);}
119                 | FETCHALL              {current.fetchall = ($1==FLAG_TRUE);}
120                 | REWRITE               {current.norewrite = ($1==FLAG_TRUE);}
121                 ;
122 %%
123
124 /* lexer interface */
125 extern char *rcfile;
126 extern int prc_lineno;
127 extern char *yytext;
128 extern FILE *yyin;
129
130 static struct hostrec *hosttail;        /* where to add new elements */
131
132 /******************************************************************
133   function:     yyerror
134   description:  report a syntax error
135   arguments:
136     s           error string
137
138   ret. value:   none
139   globals:      none
140  *****************************************************************/
141
142 yyerror (s)
143 char *s;
144 {
145   fprintf(stderr,"%s line %d: %s at %s\n", rcfile, prc_lineno, s, yytext);
146   prc_errflag++;
147 }
148
149 /******************************************************************
150   function:     prc_filecheck
151   description:  Check that a configuration file is secure
152   arguments:
153     pathname    pathname for the configuration file
154
155   ret. value:   error code.
156   globals:      none
157  *****************************************************************/
158
159 int prc_filecheck(pathname)
160 char *pathname;
161 {
162     struct stat statbuf;
163
164     /* the run control file must have the same uid as the REAL uid of this 
165        process, it must have permissions no greater than 600, and it must not 
166        be a symbolic link.  We check these conditions here. */
167
168     errno = 0;
169     if (lstat(pathname, &statbuf) < 0) {
170         if (errno == ENOENT) 
171             return(0);
172         else {
173             perror(pathname);
174             return(PS_IOERR);
175         }
176     }
177
178     if ((statbuf.st_mode & S_IFLNK) == S_IFLNK) {
179         fprintf(stderr, "File %s must not be a symbolic link.\n", pathname);
180         return(PS_AUTHFAIL);
181     }
182
183     if (statbuf.st_mode & ~(S_IFREG | S_IREAD | S_IWRITE)) {
184         fprintf(stderr, "File %s must have no more than -rw------ permissions.\n", 
185                 pathname);
186         return(PS_AUTHFAIL);
187     }
188
189     if (statbuf.st_uid != getuid()) {
190         fprintf(stderr, "File %s must be owned by you.\n", pathname);
191         return(PS_AUTHFAIL);
192     }
193
194     return(0);
195 }
196
197 /******************************************************************
198   function:     prc_parse_file
199   description:  Read the contents of the configuration file, storing 
200                 each parsed record in a linked list.
201   arguments:
202     pathname    pathname for the configuration file
203
204   ret. value:   error code.
205   globals:      writes sp_head, writes hosttail, writes yyin,
206                 writes rcfile, writes prc_errflag.
207   calls:        prc_reset, yyparse.
208  *****************************************************************/
209
210 prc_parse_file (pathname)
211 char *pathname;
212 {
213     prc_errflag = 0;
214     hostlist = hosttail = (struct hostrec *)NULL;
215     prc_reset();
216
217     /* Check that the file is secure */
218     if ((prc_errflag = prc_filecheck(pathname)) != 0)
219         return(prc_errflag);
220
221     if (errno == ENOENT)
222         return(0);
223
224     /* Open the configuration and feed it to the lexer. */
225     if ((yyin = fopen(pathname,"r")) == (FILE *)NULL) {
226         perror(pathname);
227         return(PS_IOERR);
228     }
229
230     yyparse();          /* parse entire file */
231
232     fclose(yyin);
233
234     if (prc_errflag) 
235         return(PS_SYNTAX);
236     else
237         return(0);
238 }
239
240 /******************************************************************
241   function:     prc_reset
242   description:  clear the global current record (server parameters)
243                 used by the parser.
244   arguments:    none.
245   ret. value:   none.
246   globals:      writes current.
247   calls:        none.
248  *****************************************************************/
249
250 prc_reset()
251 {
252     char        savename[HOSTLEN+1];
253     int         saveport, saveproto, saveauth;
254
255     /*
256      * Purpose of this code is to initialize the new server block with
257      * the command-line data, but preserve whatever server name was
258      * previously set.  Also preserve server options unless the
259      * command-line explicitly overrides them.
260      */
261     (void) strcpy(savename, current.servername);
262     saveport = current.port;
263     saveproto = current.protocol;
264     saveauth = current.authenticate;
265
266     memset(&current, '\0', sizeof(current));
267
268     (void) strcpy(current.servername, savename);
269     current.protocol = saveproto;
270     current.authenticate = saveauth;
271 }
272
273 /******************************************************************
274   function:     hostalloc
275   description:  append a host record to the host list
276   arguments:
277     init        pointer to block containing initial values
278   ret. value:   new record.
279   calls:        none.
280  *****************************************************************/
281
282 struct hostrec *hostalloc(init)
283 struct hostrec *init;
284 {
285     struct hostrec *node;
286
287     /* allocate new node */
288     node = (struct hostrec *) xmalloc(sizeof(struct hostrec));
289
290     /* initialize it */
291     memcpy(node, init, sizeof(struct hostrec));
292
293     /* append to end of list */
294     if (hosttail != (struct hostrec *) 0)
295         hosttail->next = node;  /* list contains at least one element */
296     else
297         hostlist = node;        /* list is empty */
298     hosttail = node;
299     return(node);
300 }
301
302 /******************************************************************
303   function:     hostalloc
304   description:  register the parsed server params by appending
305                 them to a linked list of server param records.
306   arguments:    none
307   ret. value:   none
308   globals:      reads current.
309   calls:        hostalloc.
310  *****************************************************************/
311
312 int prc_register()
313 {
314 #define STR_FORCE(fld, len) if (cmd_opts.fld[0]) \
315                                         strcpy(current.fld, cmd_opts.fld)
316     STR_FORCE(localname, USERNAMELEN);
317     STR_FORCE(remotename, USERNAMELEN);
318     STR_FORCE(password, PASSWORDLEN);
319     STR_FORCE(mailbox, FOLDERLEN);
320     STR_FORCE(smtphost, HOSTLEN);
321     STR_FORCE(mda, MDALEN);
322 #undef STR_FORCE
323     
324 #define FLAG_FORCE(fld) if (cmd_opts.fld) current.fld = cmd_opts.fld
325     FLAG_FORCE(protocol);
326     FLAG_FORCE(keep);
327     FLAG_FORCE(flush);
328     FLAG_FORCE(fetchall);
329     FLAG_FORCE(norewrite);
330     FLAG_FORCE(skip);
331     FLAG_FORCE(port);
332     FLAG_FORCE(authenticate);
333 #undef FLAG_FORCE
334
335     (void) hostalloc(&current);
336 }
337
338 /******************************************************************
339   function:     optmerge
340   description:  Merge two options records.
341                 Empty fields in h2 are filled in from h1.
342   arguments
343     h1 h2       the records 
344     
345   ret. value:   none.
346   globals:      reads current_head.
347   calls:        none.
348
349  *****************************************************************/
350
351 void optmerge(h2, h1)
352 struct hostrec *h1;
353 struct hostrec *h2;
354 {
355 #define STR_MERGE(fld, len) if (*(h2->fld) == '\0') strcpy(h2->fld, h1->fld)
356     STR_MERGE(localname, USERNAMELEN);
357     STR_MERGE(remotename, USERNAMELEN);
358     STR_MERGE(password, PASSWORDLEN);
359     STR_MERGE(mailbox, FOLDERLEN);
360     STR_MERGE(smtphost, HOSTLEN);
361     STR_MERGE(mda, MDALEN);
362 #undef STR_MERGE
363
364 #define FLAG_MERGE(fld) if (!h2->fld) h2->fld = h1->fld
365     FLAG_MERGE(protocol);
366     FLAG_MERGE(keep);
367     FLAG_MERGE(flush);
368     FLAG_MERGE(fetchall);
369     FLAG_MERGE(norewrite);
370     FLAG_MERGE(skip);
371     FLAG_MERGE(port);
372     FLAG_MERGE(authenticate);
373 #undef FLAG_MERGE
374 }
375