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 AUTHENTICATE TIMEOUT KPOP SDPS
63 %token ENVELOPE QVIRTUAL USERNAME PASSWORD FOLDER SMTPHOST MDA BSMTP LMTP
64 %token SMTPADDRESS SMTPNAME 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
69 %token SPAMBOUNCE SHOWDOTS
70 %token <proto> PROTO AUTHTYPE
72 %token <number> NUMBER
73 %token NO KEEP FLUSH FETCHALL REWRITE FORCECR STRIPCR PASS8BITS
74 %token DROPSTATUS DROPDELIVERED
75 %token DNS SERVICE PORT UIDL INTERVAL MIMEDECODE IDLE CHECKALIAS
76 %token SSL SSLKEY SSLCERT SSLPROTO SSLCERTCK SSLCERTPATH SSLFINGERPRINT
86 statement_list : statement
87 | statement_list statement
90 optmap : MAP | /* EMPTY */;
92 /* future global options should also have the form SET <name> optmap <value> */
93 statement : SET LOGFILE optmap STRING {run.logfile = xstrdup($4);}
94 | SET IDFILE optmap STRING {run.idfile = xstrdup($4);}
95 | SET DAEMON optmap NUMBER {run.poll_interval = $4;}
96 | SET POSTMASTER optmap STRING {run.postmaster = xstrdup($4);}
97 | SET BOUNCEMAIL {run.bouncemail = TRUE;}
98 | SET NO BOUNCEMAIL {run.bouncemail = FALSE;}
99 | SET SPAMBOUNCE {run.spambounce = TRUE;}
100 | SET NO SPAMBOUNCE {run.spambounce = FALSE;}
101 | SET PROPERTIES optmap STRING {run.properties =xstrdup($4);}
102 | SET SYSLOG {run.use_syslog = TRUE;}
103 | SET INVISIBLE {run.invisible = TRUE;}
104 | SET SHOWDOTS {run.showdots = TRUE;}
107 * The way the next two productions are written depends on the fact that
108 * userspecs cannot be empty. It's a kluge to deal with files that set
109 * up a load of defaults and then have poll statements following with no
110 * user options at all.
112 | define_server serverspecs {record_current();}
113 | define_server serverspecs userspecs
115 /* detect and complain about the most common user error */
116 | define_server serverspecs userspecs serv_option
117 {yyerror(_("server option after user options"));}
120 define_server : POLL STRING {reset_server($2, FALSE);}
121 | SKIP STRING {reset_server($2, TRUE);}
122 | DEFAULTS {reset_server("defaults", FALSE);}
125 serverspecs : /* EMPTY */
126 | serverspecs serv_option
129 alias_list : STRING {save_str(¤t.server.akalist,$1,0);}
130 | alias_list STRING {save_str(¤t.server.akalist,$2,0);}
133 domain_list : STRING {save_str(¤t.server.localdomains,$1,0);}
134 | domain_list STRING {save_str(¤t.server.localdomains,$2,0);}
137 serv_option : AKA alias_list
138 | VIA STRING {current.server.via = xstrdup($2);}
139 | LOCALDOMAINS domain_list
140 | PROTOCOL PROTO {current.server.protocol = $2;}
142 current.server.protocol = P_POP3;
144 if (current.server.authenticate == A_PASSWORD)
146 current.server.authenticate = A_KERBEROS_V5;
148 current.server.authenticate = A_KERBEROS_V4;
149 #endif /* KERBEROS_V5 */
151 current.server.service = KPOP_PORT;
152 #else /* INET6_ENABLE */
153 current.server.port = KPOP_PORT;
154 #endif /* INET6_ENABLE */
156 | PRINCIPAL STRING {current.server.principal = xstrdup($2);}
159 current.server.protocol = P_POP3;
160 current.server.sdps = TRUE;
162 yyerror(_("SDPS not enabled."));
163 #endif /* SDPS_ENABLE */
165 | UIDL {current.server.uidl = FLAG_TRUE;}
166 | NO UIDL {current.server.uidl = FLAG_FALSE;}
167 | CHECKALIAS {current.server.checkalias = FLAG_TRUE;}
168 | NO CHECKALIAS {current.server.checkalias = FLAG_FALSE;}
171 current.server.service = $2;
172 #endif /* INET6_ENABLE */
178 sprintf(buf, "%d", port);
179 current.server.service = xstrdup(buf);
181 current.server.port = $2;
182 #endif /* INET6_ENABLE */
185 {current.server.interval = $2;}
186 | AUTHENTICATE AUTHTYPE
187 {current.server.authenticate = $2;}
189 {current.server.timeout = $2;}
190 | ENVELOPE NUMBER STRING
192 current.server.envelope =
194 current.server.envskip = $2;
198 current.server.envelope =
200 current.server.envskip = 0;
203 | QVIRTUAL STRING {current.server.qvirtual=xstrdup($2);}
209 if (net_security_strtorequest($2, &request, &requestlen))
210 yyerror(_("invalid security request"));
212 current.server.netsec = xstrdup($2);
216 yyerror(_("network-security support disabled"));
217 #endif /* NET_SECURITY */
220 #if (defined(linux) && !defined(INET6_ENABLE)) || defined(__FreeBSD__)
221 interface_parse($2, ¤t.server);
222 #else /* (defined(linux) && !defined(INET6_ENABLE)) || defined(__FreeBSD__) */
223 fprintf(stderr, _("fetchmail: interface option is only supported under Linux (without IPv6) and FreeBSD\n"));
224 #endif /* (defined(linux) && !defined(INET6_ENABLE)) || defined(__FreeBSD__) */
227 #if (defined(linux) && !defined(INET6_ENABLE)) || defined(__FreeBSD__)
228 current.server.monitor = xstrdup($2);
229 #else /* (defined(linux) && !defined(INET6_ENABLE)) || defined(__FreeBSD__) */
230 fprintf(stderr, _("fetchmail: monitor option is only supported under Linux (without IPv6) and FreeBSD\n"));
231 #endif /* (defined(linux) && !defined(INET6_ENABLE) || defined(__FreeBSD__)) */
233 | PLUGIN STRING { current.server.plugin = xstrdup($2); }
234 | PLUGOUT STRING { current.server.plugout = xstrdup($2); }
235 | DNS {current.server.dns = FLAG_TRUE;}
236 | NO DNS {current.server.dns = FLAG_FALSE;}
237 | NO ENVELOPE {current.server.envelope = STRING_DISABLED;}
238 | TRACEPOLLS {current.tracepolls = FLAG_TRUE;}
239 | NO TRACEPOLLS {current.tracepolls = FLAG_FALSE;}
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 | SMTPNAME STRING {current.smtpname = xstrdup($2);}
314 | SPAMRESPONSE num_list
315 | MDA STRING {current.mda = xstrdup($2);}
316 | BSMTP STRING {current.bsmtp = xstrdup($2);}
317 | LMTP {current.listener = LMTP_MODE;}
318 | PRECONNECT STRING {current.preconnect = xstrdup($2);}
319 | POSTCONNECT STRING {current.postconnect = xstrdup($2);}
321 | KEEP {current.keep = FLAG_TRUE;}
322 | FLUSH {current.flush = FLAG_TRUE;}
323 | FETCHALL {current.fetchall = FLAG_TRUE;}
324 | REWRITE {current.rewrite = FLAG_TRUE;}
325 | FORCECR {current.forcecr = FLAG_TRUE;}
326 | STRIPCR {current.stripcr = FLAG_TRUE;}
327 | PASS8BITS {current.pass8bits = FLAG_TRUE;}
328 | DROPSTATUS {current.dropstatus = FLAG_TRUE;}
329 | DROPDELIVERED {current.dropdelivered = FLAG_TRUE;}
330 | MIMEDECODE {current.mimedecode = FLAG_TRUE;}
331 | IDLE {current.idle = FLAG_TRUE;}
333 | SSL {current.use_ssl = FLAG_TRUE;}
334 | SSLKEY STRING {current.sslkey = xstrdup($2);}
335 | SSLCERT STRING {current.sslcert = xstrdup($2);}
336 | SSLPROTO STRING {current.sslproto = xstrdup($2);}
337 | SSLCERTCK {current.sslcertck = FLAG_TRUE;}
338 | SSLCERTPATH STRING {current.sslcertpath = xstrdup($2);}
339 | SSLFINGERPRINT STRING {current.sslfingerprint = xstrdup($2);}
341 | NO KEEP {current.keep = FLAG_FALSE;}
342 | NO FLUSH {current.flush = FLAG_FALSE;}
343 | NO FETCHALL {current.fetchall = FLAG_FALSE;}
344 | NO REWRITE {current.rewrite = FLAG_FALSE;}
345 | NO FORCECR {current.forcecr = FLAG_FALSE;}
346 | NO STRIPCR {current.stripcr = FLAG_FALSE;}
347 | NO PASS8BITS {current.pass8bits = FLAG_FALSE;}
348 | NO DROPSTATUS {current.dropstatus = FLAG_FALSE;}
349 | NO DROPDELIVERED {current.dropdelivered = FLAG_FALSE;}
350 | NO MIMEDECODE {current.mimedecode = FLAG_FALSE;}
351 | NO IDLE {current.idle = FLAG_FALSE;}
353 | NO SSL {current.use_ssl = FLAG_FALSE;}
355 | LIMIT NUMBER {current.limit = NUM_VALUE_IN($2);}
356 | WARNINGS NUMBER {current.warnings = NUM_VALUE_IN($2);}
357 | FETCHLIMIT NUMBER {current.fetchlimit = NUM_VALUE_IN($2);}
358 | BATCHLIMIT NUMBER {current.batchlimit = NUM_VALUE_IN($2);}
359 | EXPUNGE NUMBER {current.expunge = NUM_VALUE_IN($2);}
361 | PROPERTIES STRING {current.properties = xstrdup($2);}
365 /* lexer interface */
367 extern int prc_lineno;
371 static struct query *hosttail; /* where to add new elements */
373 void yyerror (const char *s)
374 /* report a syntax error */
376 report_at_line(stderr, 0, rcfile, prc_lineno, _("%s at %s"), s,
377 (yytext && yytext[0]) ? yytext : _("end of input"));
381 int prc_filecheck(const char *pathname, const flag securecheck)
382 /* check that a configuration file is secure */
389 /* special case useful for debugging purposes */
390 if (strcmp("/dev/null", pathname) == 0)
393 /* pass through the special name for stdin */
394 if (strcmp("-", pathname) == 0)
397 /* the run control file must have the same uid as the REAL uid of this
398 process, it must have permissions no greater than 600, and it must not
399 be a symbolic link. We check these conditions here. */
401 if (lstat(pathname, &statbuf) < 0) {
405 report(stderr, "lstat: %s: %s\n", pathname, strerror(errno));
410 if (!securecheck) return PS_SUCCESS;
412 if ((statbuf.st_mode & S_IFLNK) == S_IFLNK)
414 fprintf(stderr, _("File %s must not be a symbolic link.\n"), pathname);
419 if (statbuf.st_mode & ~(S_IFREG | S_IREAD | S_IWRITE | S_IEXEC | S_IXGRP))
421 fprintf(stderr, _("File %s must have no more than -rwx--x--- (0710) permissions.\n"),
425 #endif /* __BEOS__ */
428 if (statbuf.st_uid != geteuid())
430 if (statbuf.st_uid != getuid())
431 #endif /* HAVE_GETEUID */
433 fprintf(stderr, _("File %s must be owned by you.\n"), pathname);
440 int prc_parse_file (const char *pathname, const flag securecheck)
441 /* digest the configuration into a linked list of host records */
444 querylist = hosttail = (struct query *)NULL;
448 /* Check that the file is secure */
449 if ( (prc_errflag = prc_filecheck(pathname, securecheck)) != 0 )
453 * Croak if the configuration directory does not exist.
454 * This probably means an NFS mount failed and we can't
455 * see a configuration file that ought to be there.
456 * Question: is this a portable check? It's not clear
457 * that all implementations of lstat() will return ENOTDIR
458 * rather than plain ENOENT in this case...
460 if (errno == ENOTDIR)
462 else if (errno == ENOENT)
465 /* Open the configuration file and feed it to the lexer. */
466 if (strcmp(pathname, "-") == 0)
468 else if ((yyin = fopen(pathname,"r")) == (FILE *)NULL) {
469 report(stderr, "open: %s: %s\n", pathname, strerror(errno));
473 yyparse(); /* parse entire file */
475 fclose(yyin); /* not checking this should be safe, file mode was r */
483 static void reset_server(const char *name, int skip)
484 /* clear the entire global record and initialize it with a new name */
487 memset(¤t,'\0',sizeof(current));
488 current.smtp_socket = -1;
489 current.server.pollname = xstrdup(name);
490 current.server.skip = skip;
491 current.server.principal = (char *)NULL;
495 static void user_reset(void)
496 /* clear the global current record (user parameters) used by the parser */
498 struct hostdata save;
501 * Purpose of this code is to initialize the new server block, but
502 * preserve whatever server name was previously set. Also
503 * preserve server options unless the command-line explicitly
506 save = current.server;
508 memset(¤t, '\0', sizeof(current));
509 current.smtp_socket = -1;
511 current.server = save;
514 struct query *hostalloc(init)
515 /* append a host record to the host list */
516 struct query *init; /* pointer to block containing initial values */
520 /* allocate new node */
521 node = (struct query *) xmalloc(sizeof(struct query));
525 memcpy(node, init, sizeof(struct query));
528 memset(node, '\0', sizeof(struct query));
529 node->smtp_socket = -1;
532 /* append to end of list */
533 if (hosttail != (struct query *) 0)
534 hosttail->next = node; /* list contains at least one element */
536 querylist = node; /* list is empty */
540 node->server.lead_server = leadentry;
543 node->server.lead_server = NULL;
544 leadentry = &node->server;
550 static void record_current(void)
551 /* register current parameters and append to the host list */
553 (void) hostalloc(¤t);
557 /* easier to do this than cope with variations in where the library lives */
558 int yywrap(void) {return 1;}
560 /* rcfile_y.y ends here */