%{
-/* Copyright 1993-95 by Carl Harris, Jr. Copyright 1996 by Eric S. Raymond
- * All rights reserved.
+/*
+ * rcfile_y.y -- Run control file parser for fetchmail
+ *
* For license terms, see the file COPYING in this directory.
*/
-/***********************************************************************
- module: poprc_y.y
- project: popclient
- programmer: Carl Harris, ceharris@mal.com
- Extensively hacked and fixed by esr.
- description: .poprc parser
-
- ***********************************************************************/
-
#include <config.h>
#include <stdio.h>
-extern char *poprcfile;
-extern int prc_lineno;
-extern int prc_errflag;
-extern char *yytext;
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <errno.h>
+#if defined(STDC_HEADERS)
+#include <stdlib.h>
+#endif
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#include <string.h>
+
+#include "fetchmail.h"
+
+struct query cmd_opts; /* where to put command-line info */
+struct query *querylist; /* head of server list (globally visible) */
int yydebug; /* in case we didn't generate with -- debug */
+
+static struct query current; /* current server record */
+static int prc_errflag;
+
+static void record_current();
+static void user_reset();
%}
%union {
int proto;
- int flag;
+ int number;
char *sval;
}
-%token KW_SERVER KW_PROTOCOL KW_USERNAME KW_PASSWORD
-%token KW_REMOTEFOLDER KW_LOCALFOLDER KW_SMTPHOST KW_MDA KW_EOL KW_DEFAULTS
-%token <proto> PROTO_AUTO PROTO_POP2 PROTO_POP3 PROTO_IMAP PROTO_APOP PROTO_RPOP
-%token <sval> PARAM_STRING
-%token <flag> KW_KEEP KW_FLUSH KW_FETCHALL KW_REWRITE
-%type <proto> proto;
-
-/* these are actually used by the lexer */
-%token TRUE 1
-%token FALSE 0
+%token DEFAULTS POLL SKIP AKA LOCALDOMAINS PROTOCOL
+%token AUTHENTICATE TIMEOUT KPOP KERBEROS
+%token ENVELOPE USERNAME PASSWORD FOLDER SMTPHOST MDA PRECONNECT LIMIT
+%token IS HERE THERE TO MAP WILDCARD
+%token SET BATCHLIMIT FETCHLIMIT LOGFILE INTERFACE MONITOR
+%token <proto> PROTO
+%token <sval> STRING
+%token <number> NUMBER
+%token NO KEEP FLUSH FETCHALL REWRITE STRIPCR DNS PORT
%%
-rcfile: rcline
- | rcfile rcline
- ;
-
-rcline: statement KW_EOL
- ;
-
-statement:
- | define_server {prc_register(); prc_reset();}
- ;
-
-define_server: KW_SERVER PARAM_STRING server_options {prc_setserver($2);}
- | KW_SERVER PARAM_STRING {prc_setserver($2);}
- | KW_DEFAULTS server_options {prc_setserver("defaults");}
- ;
-
-server_options: serv_option_clause
- | server_options serv_option_clause
- ;
-
-serv_option_clause:
- KW_PROTOCOL proto {prc_setproto($2);}
- | KW_USERNAME PARAM_STRING {prc_remotename($2);}
- | KW_PASSWORD PARAM_STRING {prc_setpassword($2);}
- | KW_REMOTEFOLDER PARAM_STRING {prc_setremote($2);}
- | KW_LOCALFOLDER PARAM_STRING {prc_setlocal($2);}
- | KW_SMTPHOST PARAM_STRING {prc_setsmtphost($2);}
- | KW_MDA PARAM_STRING {prc_setmda($2);}
- | KW_KEEP {prc_setkeep($1);}
- | KW_FLUSH {prc_setflush($1);}
- | KW_FETCHALL {prc_setfetchall($1);}
- | KW_REWRITE {prc_setrewrite($1);}
- ;
-
-proto: PROTO_POP2
- | PROTO_POP3
- | PROTO_IMAP
- | PROTO_APOP
- | PROTO_RPOP
- ;
+rcfile : /* empty */
+ | statement_list
+ ;
+
+statement_list : statement
+ | statement_list statement
+ ;
+
+/* future global options should also have the form SET <name> <value> */
+statement : SET LOGFILE MAP STRING {logfile = xstrdup($4);}
+
+/*
+ * The way the next two productions are written depends on the fact that
+ * userspecs cannot be empty. It's a kluge to deal with files that set
+ * up a load of defaults and then have poll statements following with no
+ * user options at all.
+ */
+ | define_server serverspecs {record_current();}
+ | define_server serverspecs userspecs
+ ;
+
+define_server : POLL STRING {memset(¤t,'\0',sizeof(current));
+ save_str(¤t.server.names, -1,$2);
+ current.server.skip = FALSE;}
+ | SKIP STRING {memset(¤t,'\0',sizeof(current));
+ save_str(¤t.server.names, -1,$2);
+ current.server.skip = TRUE;}
+ | DEFAULTS {memset(¤t,'\0',sizeof(current));
+ save_str(¤t.server.names, -1,"defaults");}
+ ;
+
+serverspecs : /* EMPTY */
+ | serverspecs serv_option
+ ;
+
+alias_list : STRING {save_str(¤t.server.names,-1,$1);}
+ | alias_list STRING {save_str(¤t.server.names,-1,$2);}
+ ;
+
+domain_list : STRING {save_str(¤t.server.localdomains,-1,$1);}
+ | domain_list STRING {save_str(¤t.server.localdomains,-1,$2);}
+ ;
+
+serv_option : AKA alias_list
+ | LOCALDOMAINS domain_list
+ | PROTOCOL PROTO {current.server.protocol = $2;}
+ | PROTOCOL KPOP {
+ current.server.protocol = P_POP3;
+ current.server.authenticate = A_KERBEROS;
+ current.server.port = KPOP_PORT;
+ }
+ | PORT NUMBER {current.server.port = $2;}
+ | AUTHENTICATE PASSWORD {current.server.authenticate = A_PASSWORD;}
+ | AUTHENTICATE KERBEROS {current.server.authenticate = A_KERBEROS;}
+ | TIMEOUT NUMBER {current.server.timeout = $2;}
+ | ENVELOPE STRING {current.server.envelope = xstrdup($2);}
+ | INTERFACE STRING {
+#ifdef linux
+ interface_parse($2, ¤t.server);
+#else
+ fprintf(stderr, "fetchmail: interface option is only supported under Linux\n");
+#endif /* linux */
+ }
+ | MONITOR STRING {
+#ifdef linux
+ current.server.monitor = xstrdup($2);
+#else
+ fprintf(stderr, "fetchmail: monitor option is only supported under Linux\n");
+#endif /* linux */
+ }
+ | DNS {current.server.dns = FLAG_TRUE;}
+ | NO DNS {current.server.dns = FLAG_FALSE;}
+ | NO ENVELOPE {current.server.envelope = STRING_DISABLED;}
+ ;
+
+/*
+ * The first and only the first user spec may omit the USERNAME part.
+ * This is a backward-compatibility kluge to allow old popclient files
+ * to keep working.
+ */
+userspecs : user1opts {record_current(); user_reset();}
+ | user1opts explicits
+ {
+ record_current(); user_reset();
+ fprintf(stderr, "Warning: user entry with no `user' keyword\n");
+ }
+ | explicits
+ ;
+
+explicits : explicitdef {record_current(); user_reset();}
+ | explicits explicitdef {record_current(); user_reset();}
+ ;
+explicitdef : userdef user0opts
+ ;
+
+userdef : USERNAME STRING {current.remotename = xstrdup($2);}
+ | USERNAME mapping_list HERE
+ | USERNAME STRING THERE {current.remotename = xstrdup($2);}
+ ;
+
+user0opts : /* EMPTY */
+ | user0opts user_option
+ ;
+
+user1opts : user_option
+ | user1opts user_option
+ ;
+
+localnames : WILDCARD {current.wildcard = TRUE;}
+ | mapping_list {current.wildcard = FALSE;}
+ | mapping_list WILDCARD {current.wildcard = TRUE;}
+ ;
+
+mapping_list : mapping
+ | mapping_list mapping
+ ;
+
+mapping : STRING
+ {save_str_pair(¤t.localnames, $1, NULL);}
+ | STRING MAP STRING
+ {save_str_pair(¤t.localnames, $1, $3);}
+ ;
+
+user_option : TO localnames HERE
+ | TO localnames
+ | IS localnames HERE
+ | IS localnames
+
+ | IS STRING THERE {current.remotename = xstrdup($2);}
+ | PASSWORD STRING {current.password = xstrdup($2);}
+ | FOLDER STRING {current.mailbox = xstrdup($2);}
+ | SMTPHOST STRING {current.smtphost = xstrdup($2);}
+ | MDA STRING {current.mda = xstrdup($2);}
+ | PRECONNECT STRING {current.preconnect = xstrdup($2);}
+
+ | KEEP {current.keep = FLAG_TRUE;}
+ | FLUSH {current.flush = FLAG_TRUE;}
+ | FETCHALL {current.fetchall = FLAG_TRUE;}
+ | REWRITE {current.rewrite = FLAG_TRUE;}
+ | STRIPCR {current.stripcr = FLAG_TRUE;}
+
+ | NO KEEP {current.keep = FLAG_FALSE;}
+ | NO FLUSH {current.flush = FLAG_FALSE;}
+ | NO FETCHALL {current.fetchall = FLAG_FALSE;}
+ | NO REWRITE {current.rewrite = FLAG_FALSE;}
+ | NO STRIPCR {current.stripcr = FLAG_FALSE;}
+
+ | LIMIT NUMBER {current.limit = $2;}
+ | FETCHLIMIT NUMBER {current.fetchlimit = $2;}
+ | BATCHLIMIT NUMBER {current.batchlimit = $2;}
+ ;
%%
+/* lexer interface */
+extern char *rcfile;
+extern int prc_lineno;
+extern char *yytext;
+extern FILE *yyin;
+
+static struct query *hosttail; /* where to add new elements */
+
+void yyerror (const char *s)
+/* report a syntax error */
+{
+ fprintf(stderr,"%s line %d: %s at %s\n", rcfile, prc_lineno, s, yytext);
+ prc_errflag++;
+}
+
+int prc_filecheck(pathname)
+/* check that a configuration file is secure */
+const char *pathname; /* pathname for the configuration file */
+{
+ struct stat statbuf;
+
+ /* the run control file must have the same uid as the REAL uid of this
+ process, it must have permissions no greater than 600, and it must not
+ be a symbolic link. We check these conditions here. */
-yyerror (s)
-char *s;
+ errno = 0;
+ if (lstat(pathname, &statbuf) < 0) {
+ if (errno == ENOENT)
+ return(0);
+ else {
+ error(0, errno, "lstat: %s", pathname);
+ return(PS_IOERR);
+ }
+ }
+
+ if ((statbuf.st_mode & S_IFLNK) == S_IFLNK) {
+ fprintf(stderr, "File %s must not be a symbolic link.\n", pathname);
+ return(PS_AUTHFAIL);
+ }
+
+ if (statbuf.st_mode & ~(S_IFREG | S_IREAD | S_IWRITE)) {
+ fprintf(stderr, "File %s must have no more than -rw------ permissions.\n",
+ pathname);
+ return(PS_AUTHFAIL);
+ }
+
+ if (statbuf.st_uid != getuid()) {
+ fprintf(stderr, "File %s must be owned by you.\n", pathname);
+ return(PS_AUTHFAIL);
+ }
+
+ return(0);
+}
+
+int prc_parse_file (pathname)
+/* digest the configuration into a linked list of host records */
+const char *pathname; /* pathname for the configuration file */
{
- fprintf(stderr,"%s line %d: %s at %s\n", poprcfile, prc_lineno, s, yytext);
- prc_errflag++;
+ prc_errflag = 0;
+ querylist = hosttail = (struct query *)NULL;
+
+ /* Check that the file is secure */
+ if ((prc_errflag = prc_filecheck(pathname)) != 0)
+ return(prc_errflag);
+
+ if (errno == ENOENT)
+ return(0);
+
+ /* Open the configuration and feed it to the lexer. */
+ if ((yyin = fopen(pathname,"r")) == (FILE *)NULL) {
+ error(0, errno, "open: %s", pathname);
+ return(PS_IOERR);
+ }
+
+ yyparse(); /* parse entire file */
+
+ fclose(yyin);
+
+ if (prc_errflag)
+ return(PS_SYNTAX);
+ else
+ return(0);
+}
+
+static void user_reset(void)
+/* clear the global current record (server parameters) used by the parser */
+{
+ struct hostdata save;
+
+ /*
+ * Purpose of this code is to initialize the new server block, but
+ * preserve whatever server name was previously set. Also
+ * preserve server options unless the command-line explicitly
+ * overrides them.
+ */
+ save = current.server;
+
+ memset(¤t, '\0', sizeof(current));
+
+ current.server = save;
}
+
+struct query *hostalloc(init)
+/* append a host record to the host list */
+struct query *init; /* pointer to block containing initial values */
+{
+ struct query *node;
+
+ /* allocate new node */
+ node = (struct query *) xmalloc(sizeof(struct query));
+
+ /* initialize it */
+ memcpy(node, init, sizeof(struct query));
+
+ /* append to end of list */
+ if (hosttail != (struct query *) 0)
+ hosttail->next = node; /* list contains at least one element */
+ else
+ querylist = node; /* list is empty */
+ hosttail = node;
+ return(node);
+}
+
+static void record_current(void)
+/* register current parameters and append to the host list */
+{
+#define FLAG_FORCE(fld) if (cmd_opts.fld) current.fld = cmd_opts.fld
+ FLAG_FORCE(server.protocol);
+ FLAG_FORCE(server.port);
+ FLAG_FORCE(server.authenticate);
+ FLAG_FORCE(server.timeout);
+ FLAG_FORCE(server.envelope);
+ FLAG_FORCE(server.skip);
+ FLAG_FORCE(server.dns);
+
+#ifdef linux
+ FLAG_FORCE(server.interface);
+ FLAG_FORCE(server.monitor);
+ FLAG_FORCE(server.interface_pair);
+#endif /* linux */
+
+ FLAG_FORCE(remotename);
+ FLAG_FORCE(password);
+ FLAG_FORCE(mailbox);
+ FLAG_FORCE(smtphost);
+ FLAG_FORCE(mda);
+ FLAG_FORCE(preconnect);
+
+ FLAG_FORCE(keep);
+ FLAG_FORCE(flush);
+ FLAG_FORCE(fetchall);
+ FLAG_FORCE(rewrite);
+ FLAG_FORCE(stripcr);
+ FLAG_FORCE(limit);
+ FLAG_FORCE(fetchlimit);
+ FLAG_FORCE(batchlimit);
+
+#undef FLAG_FORCE
+
+ (void) hostalloc(¤t);
+}
+
+void optmerge(struct query *h2, struct query *h1)
+/* merge two options records; empty fields in h2 are filled in from h1 */
+{
+ append_str_list(&h2->server.localdomains, &h1->server.localdomains);
+ append_str_list(&h2->localnames, &h1->localnames);
+
+#define FLAG_MERGE(fld) if (!h2->fld) h2->fld = h1->fld
+ FLAG_MERGE(server.protocol);
+ FLAG_MERGE(server.port);
+ FLAG_MERGE(server.authenticate);
+ FLAG_MERGE(server.timeout);
+ FLAG_MERGE(server.envelope);
+ FLAG_MERGE(server.skip);
+ FLAG_MERGE(server.dns);
+
+#ifdef linux
+ FLAG_MERGE(server.interface);
+ FLAG_MERGE(server.monitor);
+ FLAG_MERGE(server.interface_pair);
+#endif /* linux */
+
+ FLAG_MERGE(remotename);
+ FLAG_MERGE(password);
+ FLAG_MERGE(mailbox);
+ FLAG_MERGE(smtphost);
+ FLAG_MERGE(mda);
+ FLAG_MERGE(preconnect);
+
+ FLAG_MERGE(keep);
+ FLAG_MERGE(flush);
+ FLAG_MERGE(fetchall);
+ FLAG_MERGE(rewrite);
+ FLAG_MERGE(stripcr);
+ FLAG_MERGE(limit);
+ FLAG_MERGE(fetchlimit);
+ FLAG_MERGE(batchlimit);
+#undef FLAG_MERGE
+}
+
+/* easier to do this than cope with variations in where the library lives */
+int yywrap(void) {return 1;}
+
+/* rcfile_y.y ends here */