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)
25 #if defined(__CYGWIN__)
26 #include <sys/cygwin.h>
27 #endif /* __CYGWIN__ */
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 /* these should be of size PATH_MAX */
51 char currentwd[1024] = "", rcfiledir[1024] = "";
53 /* using Bison, this arranges that yydebug messages will show actual tokens */
55 #define YYPRINT(fp, type, val) fprintf(fp, " = \"%s\"", yytext)
64 %token DEFAULTS POLL SKIP VIA AKA LOCALDOMAINS PROTOCOL
65 %token AUTHENTICATE TIMEOUT KPOP SDPS ENVELOPE QVIRTUAL
66 %token PINENTRY_TIMEOUT PWMD_SOCKET PWMD_FILE USERNAME PASSWORD FOLDER SMTPHOST FETCHDOMAINS MDA BSMTP LMTP
67 %token SMTPADDRESS SMTPNAME SPAMRESPONSE PRECONNECT POSTCONNECT LIMIT WARNINGS
68 %token INTERFACE MONITOR PLUGIN PLUGOUT
69 %token IS HERE THERE TO MAP WILDCARD
70 %token BATCHLIMIT FETCHLIMIT FETCHSIZELIMIT FASTUIDL EXPUNGE PROPERTIES
71 %token SET LOGFILE DAEMON SYSLOG IDFILE PIDFILE INVISIBLE POSTMASTER BOUNCEMAIL
72 %token SPAMBOUNCE SOFTBOUNCE SHOWDOTS
73 %token BADHEADER ACCEPT REJECT_
74 %token <proto> PROTO AUTHTYPE
76 %token <number> NUMBER
77 %token NO KEEP FLUSH LIMITFLUSH FETCHALL REWRITE FORCECR STRIPCR PASS8BITS
78 %token DROPSTATUS DROPDELIVERED
79 %token DNS SERVICE PORT UIDL INTERVAL MIMEDECODE IDLE CHECKALIAS
80 %token SSL SSLKEY SSLCERT SSLPROTO SSLCERTCK SSLCERTFILE SSLCERTPATH SSLCOMMONNAME SSLFINGERPRINT
81 %token PRINCIPAL ESMTPNAME ESMTPPASSWORD
86 %destructor { free ($$); } STRING
94 statement_list : statement
95 | statement_list statement
98 optmap : MAP | /* EMPTY */;
100 /* future global options should also have the form SET <name> optmap <value> */
101 statement : SET LOGFILE optmap STRING {run.logfile = prependdir ($4, rcfiledir); free($4);}
102 | SET IDFILE optmap STRING {run.idfile = prependdir ($4, rcfiledir); free($4);}
103 | SET PIDFILE optmap STRING {run.pidfile = prependdir ($4, rcfiledir); free($4);}
104 | SET DAEMON optmap NUMBER {run.poll_interval = $4;}
105 | SET POSTMASTER optmap STRING {run.postmaster = $4;}
106 | SET BOUNCEMAIL {run.bouncemail = TRUE;}
107 | SET NO BOUNCEMAIL {run.bouncemail = FALSE;}
108 | SET SPAMBOUNCE {run.spambounce = TRUE;}
109 | SET NO SPAMBOUNCE {run.spambounce = FALSE;}
110 | SET SOFTBOUNCE {run.softbounce = TRUE;}
111 | SET NO SOFTBOUNCE {run.softbounce = FALSE;}
112 | SET PROPERTIES optmap STRING {run.properties = $4;}
113 | SET SYSLOG {run.use_syslog = TRUE;}
114 | SET NO SYSLOG {run.use_syslog = FALSE;}
115 | SET INVISIBLE {run.invisible = TRUE;}
116 | SET NO INVISIBLE {run.invisible = FALSE;}
117 | SET SHOWDOTS {run.showdots = FLAG_TRUE;}
118 | SET NO SHOWDOTS {run.showdots = FLAG_FALSE;}
119 | SET PINENTRY_TIMEOUT optmap NUMBER {
121 run.pinentry_timeout = $4;
123 yyerror(GT_("pwmd not enabled"));
128 * The way the next two productions are written depends on the fact that
129 * userspecs cannot be empty. It's a kluge to deal with files that set
130 * up a load of defaults and then have poll statements following with no
131 * user options at all.
133 | define_server serverspecs {record_current();}
134 | define_server serverspecs userspecs
136 /* detect and complain about the most common user error */
137 | define_server serverspecs userspecs serv_option
138 {yyerror(GT_("server option after user options"));}
141 define_server : POLL STRING {reset_server($2, FALSE); free($2);}
142 | SKIP STRING {reset_server($2, TRUE); free($2);}
143 | DEFAULTS {reset_server("defaults", FALSE);}
146 serverspecs : /* EMPTY */
147 | serverspecs serv_option
150 alias_list : STRING {save_str(¤t.server.akalist,$1,0); free($1);}
151 | alias_list STRING {save_str(¤t.server.akalist,$2,0); free($2);}
154 domain_list : STRING {save_str(¤t.server.localdomains,$1,0); free($1);}
155 | domain_list STRING {save_str(¤t.server.localdomains,$2,0); free($2);}
158 serv_option : AKA alias_list
159 | VIA STRING {current.server.via = $2;}
160 | LOCALDOMAINS domain_list
161 | PROTOCOL PROTO {current.server.protocol = $2;}
163 current.server.protocol = P_POP3;
165 if (current.server.authenticate == A_PASSWORD)
167 current.server.authenticate = A_KERBEROS_V5;
169 current.server.authenticate = A_KERBEROS_V4;
170 #endif /* KERBEROS_V5 */
171 current.server.service = KPOP_PORT;
173 | PRINCIPAL STRING {current.server.principal = $2;}
174 | ESMTPNAME STRING {current.server.esmtp_name = $2;}
175 | ESMTPPASSWORD STRING {current.server.esmtp_password = $2;}
178 current.server.protocol = P_POP3;
179 current.server.sdps = TRUE;
181 yyerror(GT_("SDPS not enabled."));
182 #endif /* SDPS_ENABLE */
184 | UIDL {current.server.uidl = FLAG_TRUE;}
185 | NO UIDL {current.server.uidl = FLAG_FALSE;}
186 | CHECKALIAS {current.server.checkalias = FLAG_TRUE;}
187 | NO CHECKALIAS {current.server.checkalias = FLAG_FALSE;}
189 current.server.service = $2;
194 snprintf(buf, sizeof buf, "%d", port);
195 current.server.service = xstrdup(buf);
200 snprintf(buf, sizeof buf, "%d", port);
201 current.server.service = xstrdup(buf);
204 {current.server.interval = $2;}
205 | AUTHENTICATE AUTHTYPE
206 {current.server.authenticate = $2;}
208 {current.server.timeout = $2;}
209 | ENVELOPE NUMBER STRING
211 current.server.envelope = $3;
212 current.server.envskip = $2;
216 current.server.envelope = $2;
217 current.server.envskip = 0;
220 | QVIRTUAL STRING {current.server.qvirtual = $2;}
223 interface_parse($2, ¤t.server);
225 fprintf(stderr, GT_("fetchmail: interface option is only supported under Linux (without IPv6) and FreeBSD\n"));
231 current.server.monitor = $2;
233 fprintf(stderr, GT_("fetchmail: monitor option is only supported under Linux (without IPv6) and FreeBSD\n"));
237 | PLUGIN STRING { current.server.plugin = $2; }
238 | PLUGOUT STRING { current.server.plugout = $2; }
239 | DNS {current.server.dns = FLAG_TRUE;}
240 | NO DNS {current.server.dns = FLAG_FALSE;}
241 | NO ENVELOPE {current.server.envelope = STRING_DISABLED;}
242 | TRACEPOLLS {current.server.tracepolls = FLAG_TRUE;}
243 | NO TRACEPOLLS {current.server.tracepolls = FLAG_FALSE;}
244 | BADHEADER ACCEPT {current.server.badheader = BHACCEPT;}
245 | BADHEADER REJECT_ {current.server.badheader = BHREJECT;}
248 userspecs : user1opts {record_current(); user_reset();}
252 explicits : explicitdef {record_current(); user_reset();}
253 | explicits explicitdef {record_current(); user_reset();}
256 explicitdef : userdef user0opts
259 userdef : USERNAME STRING {current.remotename = $2;}
260 | USERNAME mapping_list HERE
261 | USERNAME STRING THERE {current.remotename = $2;}
264 user0opts : /* EMPTY */
265 | user0opts user_option
268 user1opts : user_option
269 | user1opts user_option
272 localnames : WILDCARD {current.wildcard = TRUE;}
273 | mapping_list {current.wildcard = FALSE;}
274 | mapping_list WILDCARD {current.wildcard = TRUE;}
277 mapping_list : mapping
278 | mapping_list mapping
281 mapping : STRING {save_str_pair(¤t.localnames, $1, NULL); free($1);}
282 | STRING MAP STRING {save_str_pair(¤t.localnames, $1, $3); free($1); free($3);}
285 folder_list : STRING {save_str(¤t.mailboxes,$1,0); free($1);}
286 | folder_list STRING {save_str(¤t.mailboxes,$2,0); free($2);}
289 smtp_list : STRING {save_str(¤t.smtphunt, $1,TRUE); free($1);}
290 | smtp_list STRING {save_str(¤t.smtphunt, $2,TRUE); free($2);}
293 fetch_list : STRING {save_str(¤t.domainlist, $1,TRUE); free($1);}
294 | fetch_list STRING {save_str(¤t.domainlist, $2,TRUE); free($2);}
300 id = save_str(¤t.antispam,STRING_DUMMY,0);
301 id->val.status.num = $1;
306 id = save_str(¤t.antispam,STRING_DUMMY,0);
307 id->val.status.num = $2;
311 user_option : TO localnames HERE
316 | IS STRING THERE {current.remotename = $2;}
317 | PASSWORD STRING {current.password = $2;}
320 | FETCHDOMAINS fetch_list
321 | SMTPADDRESS STRING {current.smtpaddress = $2;}
322 | SMTPNAME STRING {current.smtpname = $2;}
323 | SPAMRESPONSE num_list
324 | MDA STRING {current.mda = $2;}
325 | BSMTP STRING {current.bsmtp = prependdir ($2, rcfiledir); free($2);}
326 | LMTP {current.listener = LMTP_MODE;}
327 | PRECONNECT STRING {current.preconnect = $2;}
328 | POSTCONNECT STRING {current.postconnect = $2;}
330 | KEEP {current.keep = FLAG_TRUE;}
331 | FLUSH {current.flush = FLAG_TRUE;}
332 | LIMITFLUSH {current.limitflush = FLAG_TRUE;}
333 | FETCHALL {current.fetchall = FLAG_TRUE;}
334 | REWRITE {current.rewrite = FLAG_TRUE;}
335 | FORCECR {current.forcecr = FLAG_TRUE;}
336 | STRIPCR {current.stripcr = FLAG_TRUE;}
337 | PASS8BITS {current.pass8bits = FLAG_TRUE;}
338 | DROPSTATUS {current.dropstatus = FLAG_TRUE;}
339 | DROPDELIVERED {current.dropdelivered = FLAG_TRUE;}
340 | MIMEDECODE {current.mimedecode = FLAG_TRUE;}
341 | IDLE {current.idle = FLAG_TRUE;}
345 current.use_ssl = FLAG_TRUE;
347 yyerror(GT_("SSL is not enabled"));
350 | SSLKEY STRING {current.sslkey = prependdir ($2, rcfiledir); free($2);}
351 | SSLCERT STRING {current.sslcert = prependdir ($2, rcfiledir); free($2);}
352 | SSLPROTO STRING {current.sslproto = $2;}
353 | SSLCERTCK {current.sslcertck = FLAG_TRUE;}
354 | SSLCERTFILE STRING {current.sslcertfile = prependdir($2, rcfiledir); free($2);}
355 | SSLCERTPATH STRING {current.sslcertpath = prependdir($2, rcfiledir); free($2);}
356 | SSLCOMMONNAME STRING {current.sslcommonname = $2;}
357 | SSLFINGERPRINT STRING {current.sslfingerprint = $2;}
359 | NO KEEP {current.keep = FLAG_FALSE;}
360 | NO FLUSH {current.flush = FLAG_FALSE;}
361 | NO LIMITFLUSH {current.limitflush = FLAG_FALSE;}
362 | NO FETCHALL {current.fetchall = FLAG_FALSE;}
363 | NO REWRITE {current.rewrite = FLAG_FALSE;}
364 | NO FORCECR {current.forcecr = FLAG_FALSE;}
365 | NO STRIPCR {current.stripcr = FLAG_FALSE;}
366 | NO PASS8BITS {current.pass8bits = FLAG_FALSE;}
367 | NO DROPSTATUS {current.dropstatus = FLAG_FALSE;}
368 | NO DROPDELIVERED {current.dropdelivered = FLAG_FALSE;}
369 | NO MIMEDECODE {current.mimedecode = FLAG_FALSE;}
370 | NO IDLE {current.idle = FLAG_FALSE;}
372 | NO SSL {current.use_ssl = FLAG_FALSE;}
374 | LIMIT NUMBER {current.limit = NUM_VALUE_IN($2);}
375 | WARNINGS NUMBER {current.warnings = NUM_VALUE_IN($2);}
376 | FETCHLIMIT NUMBER {current.fetchlimit = NUM_VALUE_IN($2);}
377 | FETCHSIZELIMIT NUMBER {current.fetchsizelimit = NUM_VALUE_IN($2);}
378 | FASTUIDL NUMBER {current.fastuidl = NUM_VALUE_IN($2);}
379 | BATCHLIMIT NUMBER {current.batchlimit = NUM_VALUE_IN($2);}
380 | EXPUNGE NUMBER {current.expunge = NUM_VALUE_IN($2);}
382 | PROPERTIES STRING {current.properties = $2;}
384 | PWMD_SOCKET STRING {
386 current.pwmd_socket = xstrdup($2);
388 yyerror(GT_("pwmd not enabled"));
394 current.pwmd_file = xstrdup($2);
396 yyerror(GT_("pwmd not enabled"));
402 /* lexer interface */
404 extern int prc_lineno;
408 static struct query *hosttail; /* where to add new elements */
410 void yyerror (const char *s)
411 /* report a syntax error */
413 report_at_line(stderr, 0, rcfile, prc_lineno, GT_("%s at %s"), s,
414 (yytext && yytext[0]) ? yytext : GT_("end of input"));
418 /** check that a configuration file is secure, returns PS_* status codes */
419 int prc_filecheck(const char *pathname,
420 const flag securecheck /** shortcuts permission, filetype and uid tests if false */)
427 /* special case useful for debugging purposes */
428 if (strcmp("/dev/null", pathname) == 0)
431 /* pass through the special name for stdin */
432 if (strcmp("-", pathname) == 0)
435 /* the run control file must have the same uid as the REAL uid of this
436 process, it must have permissions no greater than 600, and it must not
437 be a symbolic link. We check these conditions here. */
439 if (stat(pathname, &statbuf) < 0) {
443 report(stderr, "lstat: %s: %s\n", pathname, strerror(errno));
448 if (!securecheck) return PS_SUCCESS;
450 if (!S_ISREG(statbuf.st_mode))
452 fprintf(stderr, GT_("File %s must be a regular file.\n"), pathname);
458 if (cygwin_internal(CW_CHECK_NTSEC, pathname))
459 #endif /* __CYGWIN__ */
460 if (statbuf.st_mode & (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH | S_IXOTH))
462 fprintf(stderr, GT_("File %s must have no more than -rwx------ (0700) permissions.\n"),
466 #endif /* __BEOS__ */
469 if (statbuf.st_uid != geteuid())
471 if (statbuf.st_uid != getuid())
472 #endif /* HAVE_GETEUID */
474 fprintf(stderr, GT_("File %s must be owned by you.\n"), pathname);
481 int prc_parse_file (const char *pathname, const flag securecheck)
482 /* digest the configuration into a linked list of host records */
485 querylist = hosttail = (struct query *)NULL;
489 /* Check that the file is secure */
490 if ( (prc_errflag = prc_filecheck(pathname, securecheck)) != 0 )
494 * Croak if the configuration directory does not exist.
495 * This probably means an NFS mount failed and we can't
496 * see a configuration file that ought to be there.
497 * Question: is this a portable check? It's not clear
498 * that all implementations of lstat() will return ENOTDIR
499 * rather than plain ENOENT in this case...
501 if (errno == ENOTDIR)
503 else if (errno == ENOENT)
506 /* Open the configuration file and feed it to the lexer. */
507 if (strcmp(pathname, "-") == 0)
509 else if ((yyin = fopen(pathname,"r")) == (FILE *)NULL) {
510 report(stderr, "open: %s: %s\n", pathname, strerror(errno));
514 yyparse(); /* parse entire file */
516 fclose(yyin); /* not checking this should be safe, file mode was r */
524 static void reset_server(const char *name, int skip)
525 /* clear the entire global record and initialize it with a new name */
528 memset(¤t,'\0',sizeof(current));
529 current.smtp_socket = -1;
530 current.server.pollname = xstrdup(name);
531 current.server.skip = skip;
532 current.server.principal = (char *)NULL;
536 static void user_reset(void)
537 /* clear the global current record (user parameters) used by the parser */
539 struct hostdata save;
542 * Purpose of this code is to initialize the new server block, but
543 * preserve whatever server name was previously set. Also
544 * preserve server options unless the command-line explicitly
547 save = current.server;
549 memset(¤t, '\0', sizeof(current));
550 current.smtp_socket = -1;
552 current.server = save;
555 /** append a host record to the host list */
556 struct query *hostalloc(struct query *init /** pointer to block containing
561 /* allocate new node */
562 node = (struct query *) xmalloc(sizeof(struct query));
566 memcpy(node, init, sizeof(struct query));
569 memset(node, '\0', sizeof(struct query));
570 node->smtp_socket = -1;
573 /* append to end of list */
574 if (hosttail != (struct query *) 0)
575 hosttail->next = node; /* list contains at least one element */
577 querylist = node; /* list is empty */
581 node->server.lead_server = leadentry;
584 node->server.lead_server = NULL;
585 leadentry = &node->server;
591 static void record_current(void)
592 /* register current parameters and append to the host list */
594 (void) hostalloc(¤t);
598 char *prependdir (const char *file, const char *dir)
599 /* if a filename is relative to dir, convert it to an absolute path */
602 if (!file[0] || /* null path */
603 file[0] == '/' || /* absolute path */
604 strcmp(file, "-") == 0 || /* stdin/stdout */
605 !dir[0]) /* we don't HAVE_GETCWD */
606 return xstrdup (file);
607 newfile = (char *)xmalloc (strlen (dir) + 1 + strlen (file) + 1);
608 if (dir[strlen(dir) - 1] != '/')
609 sprintf (newfile, "%s/%s", dir, file);
611 sprintf (newfile, "%s%s", dir, file);
615 /* rcfile_y.y ends here */