* For license terms, see the file COPYING in this directory.
*/
-#include <config.h>
+#include "config.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/file.h>
+#if defined(HAVE_SYS_WAIT_H)
#include <sys/wait.h>
+#endif
#include <sys/stat.h>
#include <errno.h>
#if defined(STDC_HEADERS)
#endif
#include <string.h>
+#if NET_SECURITY
+#include <net/security.h>
+#endif /* NET_SECURITY */
+
#include "fetchmail.h"
-struct query cmd_opts; /* where to put command-line info */
+/* parser reads these */
+char *rcfile; /* path name of rc file */
+struct query cmd_opts; /* where to put command-line info */
+
+/* parser sets these */
struct query *querylist; /* head of server list (globally visible) */
-int yydebug; /* in case we didn't generate with -- debug */
+int yydebug; /* in case we didn't generate with -- debug */
-static struct query current; /* current server record */
+static struct query current; /* current server record */
static int prc_errflag;
+static struct hostdata *leadentry;
+static flag trailer;
+
+static void record_current();
+static void user_reset();
+static void reset_server(char *name, int skip);
-static void prc_register();
-static void prc_reset();
+/* using Bison, this arranges that yydebug messages will show actual tokens */
+extern char * yytext;
+#define YYPRINT(fp, type, val) fprintf(fp, " = \"%s\"", yytext)
%}
%union {
int proto;
- int flag;
int number;
char *sval;
}
-%token DEFAULTS POLL SKIP AKA LOCALDOMAINS PROTOCOL
-%token AUTHENTICATE TIMEOUT KPOP KERBEROS
-%token ENVELOPE USERNAME PASSWORD FOLDER SMTPHOST MDA PRECONNECT LIMIT
+%token DEFAULTS POLL SKIP VIA AKA LOCALDOMAINS PROTOCOL
+%token AUTHENTICATE TIMEOUT KPOP SDPS KERBEROS4 KERBEROS5 KERBEROS
+%token ENVELOPE QVIRTUAL USERNAME PASSWORD FOLDER SMTPHOST MDA SMTPADDRESS
+%token SPAMRESPONSE PRECONNECT POSTCONNECT LIMIT
+%token NETSEC INTERFACE MONITOR
%token IS HERE THERE TO MAP WILDCARD
-%token SET BATCHLIMIT FETCHLIMIT LOGFILE INTERFACE MONITOR
+%token BATCHLIMIT FETCHLIMIT EXPUNGE
+%token SET LOGFILE DAEMON SYSLOG IDFILE INVISIBLE POSTMASTER
%token <proto> PROTO
%token <sval> STRING
%token <number> NUMBER
-%token <flag> KEEP FLUSH FETCHALL REWRITE PORT
-
-/* these are actually used by the lexer */
-%token FLAG_TRUE 2
-%token FLAG_FALSE 1
+%token NO KEEP FLUSH FETCHALL REWRITE FORCECR STRIPCR PASS8BITS DROPSTATUS
+%token DNS SERVICE PORT UIDL INTERVAL MIMEDECODE CHECKALIAS
%%
| statement_list statement
;
-/* future global options should also have the form SET <name> <value> */
-statement : SET BATCHLIMIT MAP NUMBER {batchlimit = $4;}
- | SET LOGFILE MAP STRING {logfile = xstrdup($4);}
- | SET INTERFACE MAP STRING {interface = xstrdup($4);}
- | SET MONITOR MAP STRING {monitor = xstrdup($4);}
+optmap : MAP | /* EMPTY */;
+
+/* future global options should also have the form SET <name> optmap <value> */
+statement : SET LOGFILE optmap STRING {run.logfile = xstrdup($4);}
+ | SET IDFILE optmap STRING {run.idfile = xstrdup($4);}
+ | SET DAEMON optmap NUMBER {run.poll_interval = $4;}
+ | SET POSTMASTER optmap STRING {run.postmaster = xstrdup($4);}
+ | SET SYSLOG {run.use_syslog = TRUE;}
+ | SET INVISIBLE {run.invisible = TRUE;}
/*
* The way the next two productions are written depends on the fact that
* up a load of defaults and then have poll statements following with no
* user options at all.
*/
- | define_server serverspecs {prc_register(); prc_reset();}
+ | define_server serverspecs {record_current();}
| define_server serverspecs userspecs
+
+/* detect and complain about the most common user error */
+ | define_server serverspecs userspecs serv_option
+ {yyerror("server option after user options");}
;
-define_server : POLL STRING {current.servernames = (struct idlist *)NULL;
- save_str(¤t.servernames, -1, $2);
- current.skip = FALSE;}
- | SKIP STRING {current.servernames = (struct idlist *)NULL;
- save_str(¤t.servernames, -1, $2);
- current.skip = TRUE;}
- | DEFAULTS {current.servernames = (struct idlist *)NULL;
- save_str(¤t.servernames, -1,"defaults");}
+define_server : POLL STRING {reset_server($2, FALSE);}
+ | SKIP STRING {reset_server($2, TRUE);}
+ | DEFAULTS {reset_server("defaults", FALSE);}
;
serverspecs : /* EMPTY */
| serverspecs serv_option
;
-alias_list : STRING {save_str(¤t.servernames,-1,$1);}
- | alias_list STRING {save_str(¤t.servernames,-1,$2);}
+alias_list : STRING {save_str(¤t.server.akalist,$1,0);}
+ | alias_list STRING {save_str(¤t.server.akalist,$2,0);}
;
-domain_list : STRING {save_str(¤t.localdomains,-1,$1);}
- | domain_list STRING {save_str(¤t.localdomains,-1,$2);}
+domain_list : STRING {save_str(¤t.server.localdomains,$1,0);}
+ | domain_list STRING {save_str(¤t.server.localdomains,$2,0);}
;
serv_option : AKA alias_list
+ | VIA STRING {current.server.via = xstrdup($2);}
| LOCALDOMAINS domain_list
- | PROTOCOL PROTO {current.protocol = $2;}
+ | PROTOCOL PROTO {current.server.protocol = $2;}
| PROTOCOL KPOP {
- current.protocol = P_POP3;
- current.authenticate = A_KERBEROS;
- current.port = KPOP_PORT;
+ current.server.protocol = P_POP3;
+#ifdef KERBEROS_V5
+ current.server.preauthenticate = A_KERBEROS_V5;
+#else
+ current.server.preauthenticate = A_KERBEROS_V4;
+#endif /* KERBEROS_V5 */
+#if INET6
+ current.server.service = KPOP_PORT;
+#else /* INET6 */
+ current.server.port = KPOP_PORT;
+#endif /* INET6 */
}
- | PORT NUMBER {current.port = $2;}
- | AUTHENTICATE PASSWORD {current.authenticate = A_PASSWORD;}
- | AUTHENTICATE KERBEROS {current.authenticate = A_KERBEROS;}
- | TIMEOUT NUMBER {current.timeout = $2;}
- | ENVELOPE STRING {current.envelope = xstrdup($2);}
+ | SDPS {
+#ifdef ENABLE_SDPS
+ current.server.protocol = P_POP3;
+ current.server.sdps = TRUE;
+#else
+ yyerror("SDPS not enabled.");
+#endif /* ENABLE_SDPS */
+ }
+ | UIDL {current.server.uidl = FLAG_TRUE;}
+ | NO UIDL {current.server.uidl = FLAG_FALSE;}
+ | CHECKALIAS {current.server.checkalias = FLAG_TRUE;}
+ | NO CHECKALIAS {current.server.checkalias = FLAG_FALSE;}
+ | SERVICE STRING {
+#if INET6
+ current.server.service = $2;
+#endif /* INET6 */
+ }
+ | PORT NUMBER {
+#if !INET6
+ current.server.port = $2;
+#endif /* !INET6 */
+ }
+ | INTERVAL NUMBER {current.server.interval = $2;}
+ | AUTHENTICATE PASSWORD {current.server.preauthenticate = A_PASSWORD;}
+ | AUTHENTICATE KERBEROS4 {current.server.preauthenticate = A_KERBEROS_V4;}
+ | AUTHENTICATE KERBEROS5 {current.server.preauthenticate = A_KERBEROS_V5;}
+ | AUTHENTICATE KERBEROS {
+#ifdef KERBEROS_V5
+ current.server.preauthenticate = A_KERBEROS_V5;
+#else
+ current.server.preauthenticate = A_KERBEROS_V4;
+#endif /* KERBEROS_V5 */
+ }
+ | TIMEOUT NUMBER {current.server.timeout = $2;}
+
+ | ENVELOPE NUMBER STRING
+ {
+ current.server.envelope =
+ xstrdup($3);
+ current.server.envskip = $2;
+ }
+ | ENVELOPE STRING
+ {
+ current.server.envelope =
+ xstrdup($2);
+ current.server.envskip = 0;
+ }
+
+ | QVIRTUAL STRING {current.server.qvirtual=xstrdup($2);}
+ | NETSEC STRING {
+#ifdef NET_SECURITY
+ void *request;
+ int requestlen;
+
+ if (net_security_strtorequest($2, &request, &requestlen))
+ yyerror("invalid security request");
+ else {
+ current.server.netsec = xstrdup($2);
+ free(request);
+ }
+#else
+ yyerror("network-security support disabled");
+#endif /* NET_SECURITY */
+ }
+ | INTERFACE STRING {
+#if defined(linux) && !defined(INET6)
+ interface_parse($2, ¤t.server);
+#else /* defined(linux) && !defined(INET6) */
+ fprintf(stderr, "fetchmail: interface option is only supported under Linux\n");
+#endif /* defined(linux) && !defined(INET6) */
+ }
+ | MONITOR STRING {
+#if defined(linux) && !defined(INET6)
+ current.server.monitor = xstrdup($2);
+#else /* defined(linux) && !defined(INET6) */
+ fprintf(stderr, "fetchmail: monitor option is only supported under Linux\n");
+#endif /* defined(linux) && !defined(INET6) */
+ }
+ | 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 {prc_register(); prc_reset();}
- | user1opts explicits {prc_register(); prc_reset();}
+userspecs : user1opts {record_current(); user_reset();}
| explicits
;
-explicits : explicitdef {prc_register(); prc_reset();}
- | explicits explicitdef {prc_register(); prc_reset();}
+explicits : explicitdef {record_current(); user_reset();}
+ | explicits explicitdef {record_current(); user_reset();}
;
explicitdef : userdef user0opts
;
-userdef : USERNAME STRING {strcpy(current.remotename, $2);}
+userdef : USERNAME STRING {current.remotename = xstrdup($2);}
| USERNAME mapping_list HERE
- | USERNAME STRING THERE {strcpy(current.remotename, $2);}
+ | USERNAME STRING THERE {current.remotename = xstrdup($2);}
;
user0opts : /* EMPTY */
{save_str_pair(¤t.localnames, $1, $3);}
;
+folder_list : STRING {save_str(¤t.mailboxes,$1,0);}
+ | folder_list STRING {save_str(¤t.mailboxes,$2,0);}
+ ;
+
+smtp_list : STRING {save_str(¤t.smtphunt, $1,TRUE);}
+ | smtp_list STRING {save_str(¤t.smtphunt, $2,TRUE);}
+ ;
+
+num_list : NUMBER
+ {
+ struct idlist *id;
+ id=save_str(¤t.antispam,STRING_DUMMY,0);
+ id->val.status.num = $1;
+ }
+ | num_list NUMBER
+ {
+ struct idlist *id;
+ id=save_str(¤t.antispam,STRING_DUMMY,0);
+ id->val.status.num = $2;
+ }
+ ;
+
user_option : TO localnames HERE
| TO localnames
| IS localnames HERE
| IS localnames
- | IS STRING THERE {strcpy(current.remotename, $2);}
- | PASSWORD STRING {strcpy(current.password, $2);}
- | FOLDER STRING {strcpy(current.mailbox, $2);}
- | SMTPHOST STRING {strcpy(current.smtphost, $2);}
- | MDA STRING {strcpy(current.mda, $2);}
- | PRECONNECT STRING {strcpy(current.preconnect, $2);}
-
- | KEEP {current.keep = ($1==FLAG_TRUE);}
- | FLUSH {current.flush = ($1==FLAG_TRUE);}
- | FETCHALL {current.fetchall = ($1==FLAG_TRUE);}
- | REWRITE {current.norewrite = ($1==FLAG_TRUE);}
- | LIMIT NUMBER {current.limit = $2;}
- | FETCHLIMIT NUMBER {current.fetchlimit = $2;}
+ | IS STRING THERE {current.remotename = xstrdup($2);}
+ | PASSWORD STRING {current.password = xstrdup($2);}
+ | FOLDER folder_list
+ | SMTPHOST smtp_list
+ | SMTPADDRESS STRING {current.smtpaddress = xstrdup($2);}
+ | SPAMRESPONSE num_list
+ | MDA STRING {current.mda = xstrdup($2);}
+ | PRECONNECT STRING {current.preconnect = xstrdup($2);}
+ | POSTCONNECT STRING {current.postconnect = xstrdup($2);}
+
+ | KEEP {current.keep = FLAG_TRUE;}
+ | FLUSH {current.flush = FLAG_TRUE;}
+ | FETCHALL {current.fetchall = FLAG_TRUE;}
+ | REWRITE {current.rewrite = FLAG_TRUE;}
+ | FORCECR {current.forcecr = FLAG_TRUE;}
+ | STRIPCR {current.stripcr = FLAG_TRUE;}
+ | PASS8BITS {current.pass8bits = FLAG_TRUE;}
+ | DROPSTATUS {current.dropstatus = FLAG_TRUE;}
+ | MIMEDECODE {current.mimedecode = 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 FORCECR {current.forcecr = FLAG_FALSE;}
+ | NO STRIPCR {current.stripcr = FLAG_FALSE;}
+ | NO PASS8BITS {current.pass8bits = FLAG_FALSE;}
+ | NO DROPSTATUS {current.dropstatus = FLAG_FALSE;}
+ | NO MIMEDECODE {current.mimedecode = FLAG_FALSE;}
+
+ | LIMIT NUMBER {current.limit = NUM_VALUE($2);}
+ | FETCHLIMIT NUMBER {current.fetchlimit = NUM_VALUE($2);}
+ | BATCHLIMIT NUMBER {current.batchlimit = NUM_VALUE($2);}
+ | EXPUNGE NUMBER {current.expunge = NUM_VALUE($2);}
;
%%
void yyerror (const char *s)
/* report a syntax error */
{
- fprintf(stderr,"%s line %d: %s at %s\n", rcfile, prc_lineno, s, yytext);
+ error_at_line( 0, 0, rcfile, prc_lineno, "%s at %s", s,
+ (yytext && yytext[0]) ? yytext : "end of input");
prc_errflag++;
}
-int prc_filecheck(pathname)
+int prc_filecheck(const char *pathname, const flag securecheck)
/* check that a configuration file is secure */
-const char *pathname; /* pathname for the configuration file */
{
+#ifndef __EMX__
struct stat statbuf;
+ errno = 0;
+
+ /* special cases useful for debugging purposes */
+ if (strcmp("/dev/null", pathname) == 0)
+ return(PS_SUCCESS);
+
/* 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. */
- errno = 0;
if (lstat(pathname, &statbuf) < 0) {
if (errno == ENOENT)
- return(0);
+ return(PS_SUCCESS);
else {
error(0, errno, "lstat: %s", pathname);
return(PS_IOERR);
}
}
- if ((statbuf.st_mode & S_IFLNK) == S_IFLNK) {
+ if (!securecheck) return 0;
+
+ 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",
+ if (statbuf.st_mode & ~(S_IFREG | S_IREAD | S_IWRITE | S_IEXEC | S_IXGRP))
+ {
+ fprintf(stderr, "File %s must have no more than -rwx--x--- (0710) permissions.\n",
pathname);
return(PS_AUTHFAIL);
}
- if (statbuf.st_uid != getuid()) {
+ if (statbuf.st_uid != getuid())
+ {
fprintf(stderr, "File %s must be owned by you.\n", pathname);
return(PS_AUTHFAIL);
}
-
- return(0);
+#endif
+ return(PS_SUCCESS);
}
-int prc_parse_file (pathname)
+int prc_parse_file (const char *pathname, const flag securecheck)
/* digest the configuration into a linked list of host records */
-const char *pathname; /* pathname for the configuration file */
{
prc_errflag = 0;
querylist = hosttail = (struct query *)NULL;
- prc_reset();
+
+ errno = 0;
/* Check that the file is secure */
- if ((prc_errflag = prc_filecheck(pathname)) != 0)
+ if ( (prc_errflag = prc_filecheck(pathname, securecheck)) != 0 )
return(prc_errflag);
if (errno == ENOENT)
- return(0);
+ return(PS_SUCCESS);
/* Open the configuration and feed it to the lexer. */
if ((yyin = fopen(pathname,"r")) == (FILE *)NULL) {
if (prc_errflag)
return(PS_SYNTAX);
else
- return(0);
+ return(PS_SUCCESS);
+}
+
+static void reset_server(char *name, int skip)
+/* clear the entire global record and initialize it with a new name */
+{
+ trailer = FALSE;
+ memset(¤t,'\0',sizeof(current));
+ current.smtp_socket = -1;
+ current.server.pollname = xstrdup(name);
+ current.server.skip = skip;
}
-static void prc_reset(void)
-/* clear the global current record (server parameters) used by the parser */
+
+static void user_reset(void)
+/* clear the global current record (user parameters) used by the parser */
{
- int saveport, saveproto, saveauth, saveskip;
- struct idlist *saveservernames;
+ struct hostdata save;
/*
* Purpose of this code is to initialize the new server block, but
* preserve server options unless the command-line explicitly
* overrides them.
*/
- saveport = current.port;
- saveproto = current.protocol;
- saveauth = current.authenticate;
- saveskip = current.skip;
- saveservernames = current.servernames;
+ save = current.server;
memset(¤t, '\0', sizeof(current));
+ current.smtp_socket = -1;
- current.protocol = saveproto;
- current.authenticate = saveauth;
- current.skip = saveskip;
- current.servernames = saveservernames;
+ current.server = save;
}
struct query *hostalloc(init)
node = (struct query *) xmalloc(sizeof(struct query));
/* initialize it */
- memcpy(node, init, sizeof(struct query));
+ if (init)
+ memcpy(node, init, sizeof(struct query));
+ else
+ {
+ memset(node, '\0', sizeof(struct query));
+ node->smtp_socket = -1;
+ }
/* append to end of list */
if (hosttail != (struct query *) 0)
else
querylist = node; /* list is empty */
hosttail = node;
+
+ if (trailer)
+ node->server.lead_server = leadentry;
+ else
+ {
+ node->server.lead_server = NULL;
+ leadentry = &node->server;
+ }
+
return(node);
}
-static void prc_register(void)
+static void record_current(void)
/* register current parameters and append to the host list */
{
-#define STR_FORCE(fld, len) if (cmd_opts.fld[0]) \
- strcpy(current.fld, cmd_opts.fld)
- STR_FORCE(remotename, USERNAMELEN);
- STR_FORCE(password, PASSWORDLEN);
- STR_FORCE(mailbox, FOLDERLEN);
- STR_FORCE(smtphost, HOSTLEN);
- STR_FORCE(mda, MDALEN);
- STR_FORCE(preconnect, CMDLEN);
-#undef STR_FORCE
-
-#define FLAG_FORCE(fld) if (cmd_opts.fld) current.fld = cmd_opts.fld
- FLAG_FORCE(protocol);
- FLAG_FORCE(keep);
- FLAG_FORCE(flush);
- FLAG_FORCE(fetchall);
- FLAG_FORCE(norewrite);
- FLAG_FORCE(skip);
- FLAG_FORCE(port);
- FLAG_FORCE(authenticate);
- FLAG_FORCE(timeout);
- FLAG_FORCE(limit);
- FLAG_FORCE(fetchlimit);
-#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->localnames, &h1->localnames);
-
-#define STR_MERGE(fld, len) if (*(h2->fld) == '\0') strcpy(h2->fld, h1->fld)
- STR_MERGE(remotename, USERNAMELEN);
- STR_MERGE(password, PASSWORDLEN);
- STR_MERGE(mailbox, FOLDERLEN);
- STR_MERGE(smtphost, HOSTLEN);
- STR_MERGE(mda, MDALEN);
- STR_MERGE(preconnect, CMDLEN);
-#undef STR_MERGE
-
-#define FLAG_MERGE(fld) if (!h2->fld) h2->fld = h1->fld
- FLAG_MERGE(protocol);
- FLAG_MERGE(keep);
- FLAG_MERGE(flush);
- FLAG_MERGE(fetchall);
- FLAG_MERGE(norewrite);
- FLAG_MERGE(skip);
- FLAG_MERGE(port);
- FLAG_MERGE(authenticate);
- FLAG_MERGE(timeout);
- FLAG_MERGE(limit);
- FLAG_MERGE(fetchlimit);
-#undef FLAG_MERGE
+ trailer = TRUE;
}
/* easier to do this than cope with variations in where the library lives */
int yywrap(void) {return 1;}
/* rcfile_y.y ends here */
+
+