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"
32 /* parser reads these */
33 char *rcfile; /* path name of rc file */
34 struct query cmd_opts; /* where to put command-line info */
36 /* parser sets these */
37 struct query *querylist; /* head of server list (globally visible) */
39 int yydebug; /* in case we didn't generate with -- debug */
41 static struct query current; /* current server record */
42 static int prc_errflag;
43 static struct hostdata *leadentry;
46 static void record_current(void);
47 static void user_reset(void);
48 static void reset_server(const char *name, int skip);
50 /* using Bison, this arranges that yydebug messages will show actual tokens */
52 #define YYPRINT(fp, type, val) fprintf(fp, " = \"%s\"", yytext)
61 %token DEFAULTS POLL SKIP VIA AKA LOCALDOMAINS PROTOCOL
62 %token PREAUTHENTICATE TIMEOUT KPOP SDPS KERBEROS4 KERBEROS5 KERBEROS SSH
63 %token ENVELOPE QVIRTUAL USERNAME PASSWORD FOLDER SMTPHOST MDA BSMTP LMTP
64 %token SMTPADDRESS SPAMRESPONSE PRECONNECT POSTCONNECT LIMIT WARNINGS
65 %token NETSEC INTERFACE MONITOR PLUGIN PLUGOUT
66 %token IS HERE THERE TO MAP WILDCARD
67 %token BATCHLIMIT FETCHLIMIT EXPUNGE PROPERTIES
68 %token SET LOGFILE DAEMON SYSLOG IDFILE INVISIBLE POSTMASTER BOUNCEMAIL SHOWDOTS
71 %token <number> NUMBER
72 %token NO KEEP FLUSH FETCHALL REWRITE FORCECR STRIPCR PASS8BITS
73 %token DROPSTATUS DROPDELIVERED
74 %token DNS SERVICE PORT UIDL INTERVAL MIMEDECODE IDLE CHECKALIAS
75 %token SSL SSLKEY SSLCERT
83 statement_list : statement
84 | statement_list statement
87 optmap : MAP | /* EMPTY */;
89 /* future global options should also have the form SET <name> optmap <value> */
90 statement : SET LOGFILE optmap STRING {run.logfile = xstrdup($4);}
91 | SET IDFILE optmap STRING {run.idfile = xstrdup($4);}
92 | SET DAEMON optmap NUMBER {run.poll_interval = $4;}
93 | SET POSTMASTER optmap STRING {run.postmaster = xstrdup($4);}
94 | SET BOUNCEMAIL {run.bouncemail = TRUE;}
95 | SET NO BOUNCEMAIL {run.bouncemail = FALSE;}
96 | SET PROPERTIES optmap STRING {run.properties =xstrdup($4);}
97 | SET SYSLOG {run.use_syslog = TRUE;}
98 | SET INVISIBLE {run.invisible = TRUE;}
99 | SET SHOWDOTS {run.showdots = TRUE;}
102 * The way the next two productions are written depends on the fact that
103 * userspecs cannot be empty. It's a kluge to deal with files that set
104 * up a load of defaults and then have poll statements following with no
105 * user options at all.
107 | define_server serverspecs {record_current();}
108 | define_server serverspecs userspecs
110 /* detect and complain about the most common user error */
111 | define_server serverspecs userspecs serv_option
112 {yyerror(_("server option after user options"));}
115 define_server : POLL STRING {reset_server($2, FALSE);}
116 | SKIP STRING {reset_server($2, TRUE);}
117 | DEFAULTS {reset_server("defaults", FALSE);}
120 serverspecs : /* EMPTY */
121 | serverspecs serv_option
124 alias_list : STRING {save_str(¤t.server.akalist,$1,0);}
125 | alias_list STRING {save_str(¤t.server.akalist,$2,0);}
128 domain_list : STRING {save_str(¤t.server.localdomains,$1,0);}
129 | domain_list STRING {save_str(¤t.server.localdomains,$2,0);}
132 serv_option : AKA alias_list
133 | VIA STRING {current.server.via = xstrdup($2);}
134 | LOCALDOMAINS domain_list
135 | PROTOCOL PROTO {current.server.protocol = $2;}
137 current.server.protocol = P_POP3;
139 if (current.server.preauthenticate == A_PASSWORD)
141 current.server.preauthenticate = A_KERBEROS_V5;
143 current.server.preauthenticate = A_KERBEROS_V4;
144 #endif /* KERBEROS_V5 */
146 current.server.service = KPOP_PORT;
147 #else /* INET6_ENABLE */
148 current.server.port = KPOP_PORT;
149 #endif /* INET6_ENABLE */
153 current.server.protocol = P_POP3;
154 current.server.sdps = TRUE;
156 yyerror(_("SDPS not enabled."));
157 #endif /* SDPS_ENABLE */
159 | UIDL {current.server.uidl = FLAG_TRUE;}
160 | NO UIDL {current.server.uidl = FLAG_FALSE;}
161 | CHECKALIAS {current.server.checkalias = FLAG_TRUE;}
162 | NO CHECKALIAS {current.server.checkalias = FLAG_FALSE;}
165 current.server.service = $2;
166 #endif /* INET6_ENABLE */
172 sprintf(buf, "%d", port);
173 current.server.service = xstrdup(buf);
175 current.server.port = $2;
176 #endif /* INET6_ENABLE */
178 | INTERVAL NUMBER {current.server.interval = $2;}
179 | PREAUTHENTICATE PASSWORD {current.server.preauthenticate = A_PASSWORD;}
180 | PREAUTHENTICATE KERBEROS4 {current.server.preauthenticate = A_KERBEROS_V4;}
181 | PREAUTHENTICATE KERBEROS5 {current.server.preauthenticate = A_KERBEROS_V5;}
182 | PREAUTHENTICATE KERBEROS {
184 current.server.preauthenticate = A_KERBEROS_V5;
186 current.server.preauthenticate = A_KERBEROS_V4;
187 #endif /* KERBEROS_V5 */
189 | PREAUTHENTICATE SSH {current.server.preauthenticate = A_SSH;}
190 | TIMEOUT NUMBER {current.server.timeout = $2;}
192 | ENVELOPE NUMBER STRING
194 current.server.envelope =
196 current.server.envskip = $2;
200 current.server.envelope =
202 current.server.envskip = 0;
205 | QVIRTUAL STRING {current.server.qvirtual=xstrdup($2);}
211 if (net_security_strtorequest($2, &request, &requestlen))
212 yyerror(_("invalid security request"));
214 current.server.netsec = xstrdup($2);
218 yyerror(_("network-security support disabled"));
219 #endif /* NET_SECURITY */
222 #if (defined(linux) && !defined(INET6_ENABLE)) || defined(__FreeBSD__)
223 interface_parse($2, ¤t.server);
224 #else /* (defined(linux) && !defined(INET6_ENABLE)) || defined(__FreeBSD__) */
225 fprintf(stderr, _("fetchmail: interface option is only supported under Linux and FreeBSD\n"));
226 #endif /* (defined(linux) && !defined(INET6_ENABLE)) || defined(__FreeBSD__) */
229 #if (defined(linux) && !defined(INET6_ENABLE)) || defined(__FreeBSD__)
230 current.server.monitor = xstrdup($2);
231 #else /* (defined(linux) && !defined(INET6_ENABLE)) || defined(__FreeBSD__) */
232 fprintf(stderr, _("fetchmail: monitor option is only supported under Linux\n"));
233 #endif /* (defined(linux) && !defined(INET6_ENABLE) || defined(__FreeBSD__)) */
235 | PLUGIN STRING { current.server.plugin = xstrdup($2); }
236 | PLUGOUT STRING { current.server.plugout = xstrdup($2); }
237 | DNS {current.server.dns = FLAG_TRUE;}
238 | NO DNS {current.server.dns = FLAG_FALSE;}
239 | NO ENVELOPE {current.server.envelope = STRING_DISABLED;}
242 userspecs : user1opts {record_current(); user_reset();}
246 explicits : explicitdef {record_current(); user_reset();}
247 | explicits explicitdef {record_current(); user_reset();}
250 explicitdef : userdef user0opts
253 userdef : USERNAME STRING {current.remotename = xstrdup($2);}
254 | USERNAME mapping_list HERE
255 | USERNAME STRING THERE {current.remotename = xstrdup($2);}
258 user0opts : /* EMPTY */
259 | user0opts user_option
262 user1opts : user_option
263 | user1opts user_option
266 localnames : WILDCARD {current.wildcard = TRUE;}
267 | mapping_list {current.wildcard = FALSE;}
268 | mapping_list WILDCARD {current.wildcard = TRUE;}
271 mapping_list : mapping
272 | mapping_list mapping
276 {save_str_pair(¤t.localnames, $1, NULL);}
278 {save_str_pair(¤t.localnames, $1, $3);}
281 folder_list : STRING {save_str(¤t.mailboxes,$1,0);}
282 | folder_list STRING {save_str(¤t.mailboxes,$2,0);}
285 smtp_list : STRING {save_str(¤t.smtphunt, $1,TRUE);}
286 | smtp_list STRING {save_str(¤t.smtphunt, $2,TRUE);}
292 id=save_str(¤t.antispam,STRING_DUMMY,0);
293 id->val.status.num = $1;
298 id=save_str(¤t.antispam,STRING_DUMMY,0);
299 id->val.status.num = $2;
303 user_option : TO localnames HERE
308 | IS STRING THERE {current.remotename = xstrdup($2);}
309 | PASSWORD STRING {current.password = xstrdup($2);}
312 | SMTPADDRESS STRING {current.smtpaddress = xstrdup($2);}
313 | SPAMRESPONSE num_list
314 | MDA STRING {current.mda = xstrdup($2);}
315 | BSMTP STRING {current.bsmtp = xstrdup($2);}
316 | LMTP {current.listener = LMTP_MODE;}
317 | PRECONNECT STRING {current.preconnect = xstrdup($2);}
318 | POSTCONNECT STRING {current.postconnect = xstrdup($2);}
320 | KEEP {current.keep = FLAG_TRUE;}
321 | FLUSH {current.flush = FLAG_TRUE;}
322 | FETCHALL {current.fetchall = FLAG_TRUE;}
323 | REWRITE {current.rewrite = FLAG_TRUE;}
324 | FORCECR {current.forcecr = FLAG_TRUE;}
325 | STRIPCR {current.stripcr = FLAG_TRUE;}
326 | PASS8BITS {current.pass8bits = FLAG_TRUE;}
327 | DROPSTATUS {current.dropstatus = FLAG_TRUE;}
328 | DROPDELIVERED {current.dropdelivered = FLAG_TRUE;}
329 | MIMEDECODE {current.mimedecode = FLAG_TRUE;}
330 | IDLE {current.idle = FLAG_TRUE;}
332 | SSL {current.use_ssl = FLAG_TRUE;}
333 | SSLKEY STRING {current.sslkey = xstrdup($2);}
334 | SSLCERT STRING {current.sslcert = xstrdup($2);}
336 | NO KEEP {current.keep = FLAG_FALSE;}
337 | NO FLUSH {current.flush = FLAG_FALSE;}
338 | NO FETCHALL {current.fetchall = FLAG_FALSE;}
339 | NO REWRITE {current.rewrite = FLAG_FALSE;}
340 | NO FORCECR {current.forcecr = FLAG_FALSE;}
341 | NO STRIPCR {current.stripcr = FLAG_FALSE;}
342 | NO PASS8BITS {current.pass8bits = FLAG_FALSE;}
343 | NO DROPSTATUS {current.dropstatus = FLAG_FALSE;}
344 | NO DROPDELIVERED {current.dropdelivered = FLAG_FALSE;}
345 | NO MIMEDECODE {current.mimedecode = FLAG_FALSE;}
346 | NO IDLE {current.idle = FLAG_FALSE;}
348 | NO SSL {current.use_ssl = FLAG_FALSE;}
350 | LIMIT NUMBER {current.limit = NUM_VALUE_IN($2);}
351 | WARNINGS NUMBER {current.warnings = NUM_VALUE_IN($2);}
352 | FETCHLIMIT NUMBER {current.fetchlimit = NUM_VALUE_IN($2);}
353 | BATCHLIMIT NUMBER {current.batchlimit = NUM_VALUE_IN($2);}
354 | EXPUNGE NUMBER {current.expunge = NUM_VALUE_IN($2);}
356 | PROPERTIES STRING {current.properties = xstrdup($2);}
360 /* lexer interface */
362 extern int prc_lineno;
366 static struct query *hosttail; /* where to add new elements */
368 void yyerror (const char *s)
369 /* report a syntax error */
371 report_at_line(stderr, 0, rcfile, prc_lineno, _("%s at %s"), s,
372 (yytext && yytext[0]) ? yytext : _("end of input"));
376 int prc_filecheck(const char *pathname, const flag securecheck)
377 /* check that a configuration file is secure */
384 /* special case useful for debugging purposes */
385 if (strcmp("/dev/null", pathname) == 0)
388 /* pass through the special name for stdin */
389 if (strcmp("-", pathname) == 0)
392 /* the run control file must have the same uid as the REAL uid of this
393 process, it must have permissions no greater than 600, and it must not
394 be a symbolic link. We check these conditions here. */
396 if (lstat(pathname, &statbuf) < 0) {
400 report(stderr, "lstat: %s: %s\n", pathname, strerror(errno));
405 if (!securecheck) return PS_SUCCESS;
407 if ((statbuf.st_mode & S_IFLNK) == S_IFLNK)
409 fprintf(stderr, _("File %s must not be a symbolic link.\n"), pathname);
414 if (statbuf.st_mode & ~(S_IFREG | S_IREAD | S_IWRITE | S_IEXEC | S_IXGRP))
416 fprintf(stderr, _("File %s must have no more than -rwx--x--- (0710) permissions.\n"),
420 #endif /* __BEOS__ */
423 if (statbuf.st_uid != geteuid())
425 if (statbuf.st_uid != getuid())
426 #endif /* HAVE_GETEUID */
428 fprintf(stderr, _("File %s must be owned by you.\n"), pathname);
435 int prc_parse_file (const char *pathname, const flag securecheck)
436 /* digest the configuration into a linked list of host records */
439 querylist = hosttail = (struct query *)NULL;
443 /* Check that the file is secure */
444 if ( (prc_errflag = prc_filecheck(pathname, securecheck)) != 0 )
448 * Croak if the configuration directory does not exist.
449 * This probably means an NFS mount failed and we can't
450 * see a configuration file that ought to be there.
451 * Question: is this a portable check? It's not clear
452 * that all implementations of lstat() will return ENOTDIR
453 * rather than plain ENOENT in this case...
455 if (errno == ENOTDIR)
457 else if (errno == ENOENT)
460 /* Open the configuration file and feed it to the lexer. */
461 if (strcmp(pathname, "-") == 0)
463 else if ((yyin = fopen(pathname,"r")) == (FILE *)NULL) {
464 report(stderr, "open: %s: %s\n", pathname, strerror(errno));
468 yyparse(); /* parse entire file */
470 fclose(yyin); /* not checking this should be safe, file mode was r */
478 static void reset_server(const char *name, int skip)
479 /* clear the entire global record and initialize it with a new name */
482 memset(¤t,'\0',sizeof(current));
483 current.smtp_socket = -1;
484 current.server.pollname = xstrdup(name);
485 current.server.skip = skip;
489 static void user_reset(void)
490 /* clear the global current record (user parameters) used by the parser */
492 struct hostdata save;
495 * Purpose of this code is to initialize the new server block, but
496 * preserve whatever server name was previously set. Also
497 * preserve server options unless the command-line explicitly
500 save = current.server;
502 memset(¤t, '\0', sizeof(current));
503 current.smtp_socket = -1;
505 current.server = save;
508 struct query *hostalloc(init)
509 /* append a host record to the host list */
510 struct query *init; /* pointer to block containing initial values */
514 /* allocate new node */
515 node = (struct query *) xmalloc(sizeof(struct query));
519 memcpy(node, init, sizeof(struct query));
522 memset(node, '\0', sizeof(struct query));
523 node->smtp_socket = -1;
526 /* append to end of list */
527 if (hosttail != (struct query *) 0)
528 hosttail->next = node; /* list contains at least one element */
530 querylist = node; /* list is empty */
534 node->server.lead_server = leadentry;
537 node->server.lead_server = NULL;
538 leadentry = &node->server;
544 static void record_current(void)
545 /* register current parameters and append to the host list */
547 (void) hostalloc(¤t);
551 /* easier to do this than cope with variations in where the library lives */
552 int yywrap(void) {return 1;}
554 /* rcfile_y.y ends here */