3 * rcfile_y.y -- Run control file parser for fetchmail
5 * For license terms, see the file COPYING in this directory.
10 #include <sys/types.h>
12 #if defined(HAVE_SYS_WAIT_H)
17 #if defined(STDC_HEADERS)
20 #if defined(HAVE_UNISTD_H)
26 #include <net/security.h>
27 #endif /* NET_SECURITY */
29 #include "fetchmail.h"
31 /* parser reads these */
32 char *rcfile; /* path name of rc file */
33 struct query cmd_opts; /* where to put command-line info */
35 /* parser sets these */
36 struct query *querylist; /* head of server list (globally visible) */
38 int yydebug; /* in case we didn't generate with -- debug */
40 static struct query current; /* current server record */
41 static int prc_errflag;
42 static struct hostdata *leadentry;
45 static void record_current(void);
46 static void user_reset(void);
47 static void reset_server(const char *name, int skip);
49 /* using Bison, this arranges that yydebug messages will show actual tokens */
51 #define YYPRINT(fp, type, val) fprintf(fp, " = \"%s\"", yytext)
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
70 %token <number> NUMBER
71 %token NO KEEP FLUSH FETCHALL REWRITE FORCECR STRIPCR PASS8BITS DROPSTATUS
72 %token DNS SERVICE PORT UIDL INTERVAL MIMEDECODE CHECKALIAS
80 statement_list : statement
81 | statement_list statement
84 optmap : MAP | /* EMPTY */;
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;}
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.
101 | define_server serverspecs {record_current();}
102 | define_server serverspecs userspecs
104 /* detect and complain about the most common user error */
105 | define_server serverspecs userspecs serv_option
106 {yyerror("server option after user options");}
109 define_server : POLL STRING {reset_server($2, FALSE);}
110 | SKIP STRING {reset_server($2, TRUE);}
111 | DEFAULTS {reset_server("defaults", FALSE);}
114 serverspecs : /* EMPTY */
115 | serverspecs serv_option
118 alias_list : STRING {save_str(¤t.server.akalist,$1,0);}
119 | alias_list STRING {save_str(¤t.server.akalist,$2,0);}
122 domain_list : STRING {save_str(¤t.server.localdomains,$1,0);}
123 | domain_list STRING {save_str(¤t.server.localdomains,$2,0);}
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;}
131 current.server.protocol = P_POP3;
133 if (current.server.preauthenticate == A_PASSWORD)
135 current.server.preauthenticate = A_KERBEROS_V5;
137 current.server.preauthenticate = A_KERBEROS_V4;
138 #endif /* KERBEROS_V5 */
140 current.server.service = KPOP_PORT;
142 current.server.port = KPOP_PORT;
147 current.server.protocol = P_POP3;
148 current.server.sdps = TRUE;
150 yyerror("SDPS not enabled.");
151 #endif /* SDPS_ENABLE */
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;}
159 current.server.service = $2;
164 current.server.port = $2;
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 {
173 current.server.preauthenticate = A_KERBEROS_V5;
175 current.server.preauthenticate = A_KERBEROS_V4;
176 #endif /* KERBEROS_V5 */
178 | TIMEOUT NUMBER {current.server.timeout = $2;}
180 | ENVELOPE NUMBER STRING
182 current.server.envelope =
184 current.server.envskip = $2;
188 current.server.envelope =
190 current.server.envskip = 0;
193 | QVIRTUAL STRING {current.server.qvirtual=xstrdup($2);}
199 if (net_security_strtorequest($2, &request, &requestlen))
200 yyerror("invalid security request");
202 current.server.netsec = xstrdup($2);
206 yyerror("network-security support disabled");
207 #endif /* NET_SECURITY */
210 #if defined(linux) && !defined(INET6)
211 interface_parse($2, ¤t.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) */
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) */
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;}
230 userspecs : user1opts {record_current(); user_reset();}
234 explicits : explicitdef {record_current(); user_reset();}
235 | explicits explicitdef {record_current(); user_reset();}
238 explicitdef : userdef user0opts
241 userdef : USERNAME STRING {current.remotename = xstrdup($2);}
242 | USERNAME mapping_list HERE
243 | USERNAME STRING THERE {current.remotename = xstrdup($2);}
246 user0opts : /* EMPTY */
247 | user0opts user_option
250 user1opts : user_option
251 | user1opts user_option
254 localnames : WILDCARD {current.wildcard = TRUE;}
255 | mapping_list {current.wildcard = FALSE;}
256 | mapping_list WILDCARD {current.wildcard = TRUE;}
259 mapping_list : mapping
260 | mapping_list mapping
264 {save_str_pair(¤t.localnames, $1, NULL);}
266 {save_str_pair(¤t.localnames, $1, $3);}
269 folder_list : STRING {save_str(¤t.mailboxes,$1,0);}
270 | folder_list STRING {save_str(¤t.mailboxes,$2,0);}
273 smtp_list : STRING {save_str(¤t.smtphunt, $1,TRUE);}
274 | smtp_list STRING {save_str(¤t.smtphunt, $2,TRUE);}
280 id=save_str(¤t.antispam,STRING_DUMMY,0);
281 id->val.status.num = $1;
286 id=save_str(¤t.antispam,STRING_DUMMY,0);
287 id->val.status.num = $2;
291 user_option : TO localnames HERE
296 | IS STRING THERE {current.remotename = xstrdup($2);}
297 | PASSWORD STRING {current.password = xstrdup($2);}
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);}
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;}
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;}
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);}
334 | PROPERTIES STRING {current.properties = xstrdup($2);}
338 /* lexer interface */
340 extern int prc_lineno;
344 static struct query *hosttail; /* where to add new elements */
346 void yyerror (const char *s)
347 /* report a syntax error */
349 report_at_line(stderr, 0, rcfile, prc_lineno, "%s at %s", s,
350 (yytext && yytext[0]) ? yytext : "end of input");
354 int prc_filecheck(const char *pathname, const flag securecheck)
355 /* check that a configuration file is secure */
362 /* special case useful for debugging purposes */
363 if (strcmp("/dev/null", pathname) == 0)
366 /* pass through the special name for stdin */
367 if (strcmp("-", pathname) == 0)
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. */
374 if (lstat(pathname, &statbuf) < 0) {
378 report(stderr, errno, "lstat: %s\n", pathname);
383 if (!securecheck) return 0;
385 if ((statbuf.st_mode & S_IFLNK) == S_IFLNK)
387 fprintf(stderr, "File %s must not be a symbolic link.\n", pathname);
391 if (statbuf.st_mode & ~(S_IFREG | S_IREAD | S_IWRITE | S_IEXEC | S_IXGRP))
393 fprintf(stderr, "File %s must have no more than -rwx--x--- (0710) permissions.\n",
398 if (statbuf.st_uid != getuid())
400 fprintf(stderr, "File %s must be owned by you.\n", pathname);
407 int prc_parse_file (const char *pathname, const flag securecheck)
408 /* digest the configuration into a linked list of host records */
411 querylist = hosttail = (struct query *)NULL;
415 /* Check that the file is secure */
416 if ( (prc_errflag = prc_filecheck(pathname, securecheck)) != 0 )
422 /* Open the configuration file and feed it to the lexer. */
423 if (strcmp(pathname, "-") == 0)
425 else if ((yyin = fopen(pathname,"r")) == (FILE *)NULL) {
426 report(stderr, errno, "open: %s\n", pathname);
430 yyparse(); /* parse entire file */
440 static void reset_server(const char *name, int skip)
441 /* clear the entire global record and initialize it with a new name */
444 memset(¤t,'\0',sizeof(current));
445 current.smtp_socket = -1;
446 current.server.pollname = xstrdup(name);
447 current.server.skip = skip;
451 static void user_reset(void)
452 /* clear the global current record (user parameters) used by the parser */
454 struct hostdata save;
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
462 save = current.server;
464 memset(¤t, '\0', sizeof(current));
465 current.smtp_socket = -1;
467 current.server = save;
470 struct query *hostalloc(init)
471 /* append a host record to the host list */
472 struct query *init; /* pointer to block containing initial values */
476 /* allocate new node */
477 node = (struct query *) xmalloc(sizeof(struct query));
481 memcpy(node, init, sizeof(struct query));
484 memset(node, '\0', sizeof(struct query));
485 node->smtp_socket = -1;
488 /* append to end of list */
489 if (hosttail != (struct query *) 0)
490 hosttail->next = node; /* list contains at least one element */
492 querylist = node; /* list is empty */
496 node->server.lead_server = leadentry;
499 node->server.lead_server = NULL;
500 leadentry = &node->server;
506 static void record_current(void)
507 /* register current parameters and append to the host list */
509 (void) hostalloc(¤t);
513 /* easier to do this than cope with variations in where the library lives */
514 int yywrap(void) {return 1;}
516 /* rcfile_y.y ends here */