X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=rcfile_y.y;h=06f3cd23d0cd84125c5444897bde94c7b022ddcf;hb=db2cdc170df42f155fa63012fdaf0b7b9fc73ce9;hp=5cd20368035e84dc4ea6c12f020bc3fc548cc117;hpb=e2035c018f293db615fb4c46984ea033b9e7f50b;p=~andy%2Ffetchmail diff --git a/rcfile_y.y b/rcfile_y.y index 5cd20368..06f3cd23 100644 --- a/rcfile_y.y +++ b/rcfile_y.y @@ -1,94 +1,413 @@ %{ -/* 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 #include -extern char *poprcfile; -extern int prc_lineno; -extern int prc_errflag; -extern char *yytext; +#include +#include +#include +#include +#include +#if defined(STDC_HEADERS) +#include +#endif +#if defined(HAVE_UNISTD_H) +#include +#endif +#include + +#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_AUTO PROTO_POP2 PROTO_POP3 PROTO_IMAP PROTO_APOP PROTO_RPOP -%token PARAM_STRING -%token KW_KEEP KW_FLUSH KW_FETCHALL KW_REWRITE -%type 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 +%token STRING +%token 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 */ +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 */