/* $Id$ */
/*
* Copyright (c) 2006 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* Declarations */
%{
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <grp.h>
#include <pwd.h>
#include <string.h>
#include <syslog.h>
#include "fdm.h"
#include "deliver.h"
#include "fetch.h"
#include "match.h"
struct strb *parse_tags;
struct macros parse_macros;
struct macro *parse_last; /* last command-line argument macro */
u_int parse_ruleidx;
u_int parse_actionidx;
ARRAY_DECL(, struct rule *) parse_rulestack;
struct rule *parse_rule;
struct files parse_filestack;
struct file *parse_file;
int yyparse(void);
int
parse_conf(const char *path, struct strings *macros)
{
struct macro *macro;
FILE *f;
u_int i;
if ((f = fopen(path, "r")) == NULL)
return (-1);
ARRAY_INIT(&parse_rulestack);
parse_rule = NULL;
ARRAY_INIT(&parse_filestack);
parse_file = xmalloc(sizeof *parse_file);
parse_file->f = f;
parse_file->line = 0;
parse_file->path = path;
strb_create(&parse_tags);
default_tags(&parse_tags, NULL);
add_tag(&parse_tags, "home", "%s", conf.user_home);
TAILQ_INIT(&parse_macros);
parse_last = NULL;
for (i = 0; i < ARRAY_LENGTH(macros); i++) {
parse_last = extract_macro(ARRAY_ITEM(macros, i));
TAILQ_INSERT_TAIL(&parse_macros, parse_last, entry);
}
parse_file->line++;
yyparse();
if (!ARRAY_EMPTY(&parse_rulestack))
yyerror("missing }");
ARRAY_FREE(&parse_rulestack);
ARRAY_FREE(&parse_filestack);
xfree(parse_file);
while (!TAILQ_EMPTY(&parse_macros)) {
macro = TAILQ_FIRST(&parse_macros);
TAILQ_REMOVE(&parse_macros, macro, entry);
if (macro->type == MACRO_STRING)
xfree(macro->value.str);
xfree(macro);
}
strb_destroy(&parse_tags);
fclose(f);
return (0);
}
__dead printflike1 void
yyerror(const char *fmt, ...)
{
va_list ap;
char *s;
xasprintf(&s,
"%s: %s at line %d", parse_file->path, fmt, parse_file->line);
va_start(ap, fmt);
log_vwrite(LOG_CRIT, s, ap);
va_end(ap);
exit(1);
}
%}
%token TOKACCOUNT
%token TOKACCOUNTS
%token TOKACTION
%token TOKACTIONS
%token TOKADDHEADER
%token TOKADDTOCACHE
%token TOKAGE
%token TOKALL
%token TOKALLOWMANY
%token TOKAND
%token TOKANYNAME
%token TOKANYSIZE
%token TOKANYTYPE
%token TOKAPPEND
%token TOKATTACHMENT
%token TOKBODY
%token TOKBYTES
%token TOKCACHE
%token TOKCASE
%token TOKCMDUSER
%token TOKCOMPRESS
%token TOKCONTINUE
%token TOKCOUNT
%token TOKDAYS
%token TOKDEFUSER
%token TOKDELTOOBIG
%token TOKDISABLED
%token TOKDOMAIN
%token TOKDOTLOCK
%token TOKDROP
%token TOKEQ
%token TOKEXEC
%token TOKEXPIRE
%token TOKFCNTL
%token TOKFILEGROUP
%token TOKFILEUMASK
%token TOKFLOCK
%token TOKFOLDER
%token TOKFOLDERS
%token TOKFROM
%token TOKGIGABYTES
%token TOKGROUP
%token TOKGROUPS
%token TOKHEADER
%token TOKHEADERS
%token TOKHOURS
%token TOKIGNOREERRORS
%token TOKIMAP
%token TOKIMAPS
%token TOKIMPLACT
%token TOKIN
%token TOKINCACHE
%token TOKINSECURE
%token TOKINVALID
%token TOKKEEP
%token TOKKEY
%token TOKKILOBYTES
%token TOKLOCKFILE
%token TOKLOCKTIMEOUT
%token TOKLOCKTYPES
%token TOKLOCKWAIT
%token TOKLOOKUPORDER
%token TOKMAILDIR
%token TOKMAILDIRS
%token TOKMATCH
%token TOKMATCHED
%token TOKMAXSIZE
%token TOKMBOX
%token TOKMBOXES
%token TOKMEGABYTES
%token TOKMINUTES
%token TOKMONTHS
%token TOKNE
%token TOKNEWONLY
%token TOKNNTP
%token TOKNNTPS
%token TOKNOAPOP
%token TOKNOCRAMMD5
%token TOKNOCREATE
%token TOKNOLOGIN
%token TOKNONE
%token TOKNORECEIVED
%token TOKNOT
%token TOKNOUIDL
%token TOKNOVERIFY
%token TOKOLDONLY
%token TOKOR
%token TOKPARALLELACCOUNTS
%token TOKPASS
%token TOKPASSWD
%token TOKPIPE
%token TOKPOP3
%token TOKPOP3S
%token TOKPORT
%token TOKPROXY
%token TOKPURGEAFTER
%token TOKQUEUEHIGH
%token TOKQUEUELOW
%token TOKREMOVEFROMCACHE
%token TOKREMOVEHEADER
%token TOKREMOVEHEADERS
%token TOKRETURNS
%token TOKREWRITE
%token TOKSECONDS
%token TOKSERVER
%token TOKSET
%token TOKSIZE
%token TOKSMTP
%token TOKSTARTTLS
%token TOKSTDIN
%token TOKSTDOUT
%token TOKSTRING
%token TOKSTRIPCHARACTERS
%token TOKTAG
%token TOKTAGGED
%token TOKTIMEOUT
%token TOKTO
%token TOKTOTALSIZE
%token TOKUNMATCHED
%token TOKUSER
%token TOKUSERS
%token TOKVALUE
%token TOKVERIFYCERTS
%token TOKWEEKS
%token TOKWRITE
%token TOKYEARS
%union
{
long long number;
char *string;
int flag;
u_int locks;
struct {
struct fetch *fetch;
void *data;
} fetch;
struct {
char *host;
char *port;
} server;
enum area area;
enum exprop exprop;
struct actitem *actitem;
struct actlist *actlist;
struct expr *expr;
struct expritem *expritem;
struct strings *strings;
struct replstrs *replstrs;
enum fetch_only only;
struct {
char *path;
enum fetch_only only;
} poponly;
struct {
int flags;
char *str;
} re;
gid_t localgid;
enum cmp cmp;
struct rule *rule;
struct {
char *user;
int user_netrc;
char *pass;
int pass_netrc;
} userpass;
userfunction ufn;
struct userfunctions *ufns;
}
%token NONE
%token <number> NUMBER
%token <string> STRING STRMACRO NUMMACRO
%token <string> STRCOMMAND NUMCOMMAND
%type <actitem> actitem
%type <actlist> actlist
%type <area> area
%type <cmp> cmp ltgt eqne
%type <expr> expr exprlist
%type <expritem> expritem
%type <exprop> exprop
%type <fetch> fetchtype
%type <flag> cont not disabled keep execpipe writeappend compress verify
%type <flag> apop poptype imaptype nntptype nocrammd5 nologin uidl starttls
%type <flag> insecure
%type <localgid> localgid
%type <locks> lock locklist
%type <number> size time numv retrc expire
%type <only> only imaponly
%type <poponly> poponly
%type <replstrs> replstrslist actions rmheaders accounts users
%type <re> casere retre
%type <rule> perform
%type <server> server
%type <string> port to from xstrv strv replstrv replpathv val optval folder1
%type <string> user
%type <strings> stringslist pathslist maildirs mboxes groups folders folderlist
%type <userpass> userpass userpassreqd userpassnetrc
%type <ufn> ufn
%type <ufns> ufnlist
%%
/* Rules */
cmds: /* empty */
| cmds account
| cmds defaction
| cmds defmacro
| cmds rule
| cmds set
| cmds close
| cmds cache
| cmds NONE
/* Plural/singular combinations. */
actionp: TOKACTION
| TOKACTIONS
userp: TOKUSER
| TOKUSERS
accountp: TOKACCOUNT
| TOKACCOUNTS
groupp: TOKGROUP
| TOKGROUPS
folderp: TOKFOLDER
| TOKFOLDERS
maildirp: TOKMAILDIR
| TOKMAILDIRS
mboxp: TOKMBOX
| TOKMBOXES
rmheaderp: TOKREMOVEHEADER
| TOKREMOVEHEADERS
val: TOKVALUE strv
{
$$ = $2;
}
| strv
{
$$ = $1;
}
optval: TOKVALUE strv
{
$$ = $2;
}
| /* empty */
{
$$ = NULL;
}
xstrv: STRCOMMAND
{
$$ = run_command($1, parse_file->path);
xfree($1);
}
| STRING
{
$$ = $1;
}
| STRMACRO
{
struct macro *macro;
if (strlen($1) > MAXNAMESIZE)
yyerror("macro name too long: %s", $1);
if ((macro = find_macro($1)) == NULL)
yyerror("undefined macro: %s", $1);
if (macro->type != MACRO_STRING)
yyerror("string macro expected: %s", $1);
$$ = xstrdup(macro->value.str);
xfree($1);
}
strv: xstrv
{
$$ = $1;
}
| strv '+' xstrv
{
size_t size;
size = strlen($1) + strlen($3) + 1;
$$ = xrealloc($1, 1, size);
strlcat($$, $3, size);
xfree($3);
}
numv: NUMCOMMAND
{
const char *errstr;
char *s;
s = run_command($1, parse_file->path);
$$ = strtonum(s, 0, LLONG_MAX, &errstr);
if (errstr != NULL)
yyerror("number is %s", errstr);
xfree(s);
xfree($1);
}
| NUMBER
{
$$ = $1;
}
| NUMMACRO
{
struct macro *macro;
if (strlen($1) > MAXNAMESIZE)
yyerror("macro name too long: %s", $1);
if ((macro = find_macro($1)) == NULL)
yyerror("undefined macro: %s", $1);
if (macro->type != MACRO_NUMBER)
yyerror("number macro expected: %s", $1);
$$ = macro->value.num;
xfree($1);
}
replstrv: strv
{
struct replstr rs;
rs.str = $1;
$$ = replacestr(&rs, parse_tags, NULL, NULL);
xfree($1);
}
replpathv: strv
{
struct replpath rp;
rp.str = $1;
$$ = replacepath(&rp, parse_tags, NULL, NULL, conf.user_home);
xfree($1);
}
size: numv
{
$$ = $1;
}
| numv TOKBYTES
{
$$ = $1;
}
| numv TOKKILOBYTES
{
if ($1 > LLONG_MAX / 1024)
yyerror("size is too big");
$$ = $1 * 1024;
}
| numv TOKMEGABYTES
{
if ($1 > LLONG_MAX / (1024 * 1024))
yyerror("size is too big");
$$ = $1 * (1024 * 1024);
}
| numv TOKGIGABYTES
{
if ($1 > LLONG_MAX / (1024 * 1024 * 1024))
yyerror("size is too big");
$$ = $1 * (1024 * 1024 * 1024);
}
time: numv
{
$$ = $1;
}
| numv TOKHOURS
{
if ($1 > LLONG_MAX / TIME_HOUR)
yyerror("time is too long");
$$ = $1 * TIME_HOUR;
}
| numv TOKMINUTES
{
if ($1 > LLONG_MAX / TIME_MINUTE)
yyerror("time is too long");
$$ = $1 * TIME_MINUTE;
}
| numv TOKSECONDS
{
$$ = $1;
}
| numv TOKDAYS
{
if ($1 > LLONG_MAX / TIME_DAY)
yyerror("time is too long");
$$ = $1 * TIME_DAY;
}
| numv TOKWEEKS
{
if ($1 > LLONG_MAX / TIME_WEEK)
yyerror("time is too long");
$$ = $1 * TIME_WEEK;
}
| numv TOKMONTHS
{
if ($1 > LLONG_MAX / TIME_MONTH)
yyerror("time is too long");
$$ = $1 * TIME_MONTH;
}
| numv TOKYEARS
{
if ($1 > LLONG_MAX / TIME_YEAR)
yyerror("time is too long");
$$ = $1 * TIME_YEAR;
}
expire: TOKEXPIRE time
{
#if UINT64_MAX < LLONG_MAX
if ($2 > UINT64_MAX)
yyerror("time too long");
#endif
$$ = $2;
}
| /* empty */
{
$$ = -1;
}
cache: TOKCACHE replpathv expire
{
struct cache *cache;
TAILQ_FOREACH(cache, &conf.caches, entry) {
if (strcmp(cache->path, $2) == 0)
yyerror("duplicate cache path");
}
cache = xcalloc(1, sizeof *cache);
cache->path = $2;
cache->expire = $3;
TAILQ_INSERT_TAIL(&conf.caches, cache, entry);
log_debug2("added cache \"%s\": expire %lld", cache->path, $3);
}
set: TOKSET TOKMAXSIZE size
{
if ($3 == 0)
yyerror("zero maximum size");
if ($3 > MAXMAILSIZE)
yyerror("maximum size too large: %lld", $3);
conf.max_size = $3;
}
| TOKSET TOKLOCKTYPES locklist
{
if ($3 & LOCK_FCNTL && $3 & LOCK_FLOCK)
yyerror("fcntl and flock locking cannot be used together");
conf.lock_types = $3;
}
| TOKSET TOKLOCKFILE replpathv
{
if (conf.lock_file != NULL)
xfree(conf.lock_file);
conf.lock_file = $3;
}
| TOKSET TOKLOCKWAIT
{
conf.lock_wait = 1;
}
| TOKSET TOKLOCKTIMEOUT time
{
conf.lock_timeout = $3;
}
| TOKSET TOKDELTOOBIG
{
conf.del_big = 1;
}
| TOKSET TOKIGNOREERRORS
{
conf.ignore_errors = 1;
}
| TOKSET TOKALLOWMANY
{
conf.allow_many = 1;
}
| TOKSET TOKDEFUSER strv
{
if (conf.def_user == NULL)
conf.def_user = $3;
}
| TOKSET TOKCMDUSER strv
{
if (conf.cmd_user == NULL)
conf.cmd_user = $3;
}
| TOKSET TOKSTRIPCHARACTERS strv
{
xfree(conf.strip_chars);
conf.strip_chars = $3;
}
| TOKSET TOKTIMEOUT time
{
if ($3 == 0)
yyerror("zero timeout");
if ($3 > INT_MAX / 1000)
yyerror("timeout too long: %lld", $3);
conf.timeout = $3 * 1000;
}
| TOKSET TOKQUEUEHIGH numv
{
if ($3 == 0)
yyerror("zero queue-high");
if ($3 > MAXQUEUEVALUE)
yyerror("queue-high too big: %lld", $3);
if (conf.queue_low != -1 && $3 <= conf.queue_low)
yyerror("queue-high must be larger than queue-low");
conf.queue_high = $3;
}
| TOKSET TOKQUEUELOW numv
{
if ($3 > MAXQUEUEVALUE)
yyerror("queue-low too big: %lld", $3);
if (conf.queue_high == -1)
yyerror("queue-high not specified");
if ($3 >= conf.queue_high)
yyerror("queue-low must be smaller than queue-high");
conf.queue_low = $3;
}
| TOKSET TOKPARALLELACCOUNTS numv
{
if ($3 > INT_MAX)
yyerror("parallel-accounts too big: %lld", $3);
if ($3 == 0)
yyerror("parallel-accounts cannot be zero");
conf.max_accts = $3;
}
| TOKSET TOKPROXY replstrv
{
if (conf.proxy != NULL) {
xfree(conf.proxy->server.host);
xfree(conf.proxy->server.port);
if (conf.proxy->user != NULL)
xfree(conf.proxy->user);
if (conf.proxy->pass != NULL)
xfree(conf.proxy->pass);
}
if ((conf.proxy = getproxy($3)) == NULL)
yyerror("invalid proxy");
xfree($3);
}
| TOKSET TOKVERIFYCERTS
{
conf.verify_certs = 1;
}
| TOKSET TOKIMPLACT TOKKEEP
{
conf.impl_act = DECISION_KEEP;
}
| TOKSET TOKIMPLACT TOKDROP
{
conf.impl_act = DECISION_DROP;
}
| TOKSET TOKPURGEAFTER numv
{
if ($3 == 0)
yyerror("invalid purge-after value: 0");
if ($3 > UINT_MAX)
yyerror("purge-after value too large: %lld", $3);
conf.purge_after = $3;
}
| TOKSET TOKPURGEAFTER TOKNONE
{
conf.purge_after = 0;
}
| TOKSET TOKNORECEIVED
{
conf.no_received = 1;
}
| TOKSET TOKNOCREATE
{
conf.no_create = 1;
}
| TOKSET TOKFILEGROUP TOKUSER
{
conf.file_group = -1;
}
| TOKSET TOKFILEGROUP localgid
{
conf.file_group = $3;
}
| TOKSET TOKFILEUMASK TOKUSER
{
conf.file_umask = umask(0);
umask(conf.file_umask);
}
| TOKSET TOKLOOKUPORDER ufnlist
{
ARRAY_FREEALL(conf.user_order);
conf.user_order = $3;
}
| TOKSET TOKFILEUMASK numv
{
char s[8];
u_int n;
/*
* We can't differentiate umasks in octal from normal numbers
* (requiring a leading zero a la C would be nice, but it would
* potentially break existing configs), so we need to fiddle to
* convert.
*/
memset(s, 0, sizeof s);
xsnprintf(s, sizeof s, "%03lld", $3);
if (s[3] != '\0' || s[0] < '0' || s[0] > '7' ||
s[1] < 0 || s[1] > '7' || s[2] < '0' || s[2] > '7')
yyerror("invalid umask: %s", s);
if (sscanf(s, "%o", &n) != 1)
yyerror("invalid umask: %s", s);
conf.file_umask = n;
}
defmacro: STRMACRO '=' strv
{
struct macro *macro;
if (strlen($1) > MAXNAMESIZE)
yyerror("macro name too long: %s", $1);
macro = xmalloc(sizeof *macro);
strlcpy(macro->name, $1, sizeof macro->name);
macro->type = MACRO_STRING;
macro->value.str = $3;
if (parse_last == NULL)
TAILQ_INSERT_HEAD(&parse_macros, macro, entry);
else {
TAILQ_INSERT_AFTER(
&parse_macros, parse_last, macro, entry);
}
log_debug3("added macro \"%s\": \"%s\"", macro->name,
macro->value.str);
xfree($1);
}
| NUMMACRO '=' numv
{
struct macro *macro;
if (strlen($1) > MAXNAMESIZE)
yyerror("macro name too long: %s", $1);
macro = xmalloc(sizeof *macro);
strlcpy(macro->name, $1, sizeof macro->name);
macro->type = MACRO_NUMBER;
macro->value.num = $3;
if (parse_last == NULL)
TAILQ_INSERT_HEAD(&parse_macros, macro, entry);
else {
TAILQ_INSERT_AFTER(
&parse_macros, parse_last, macro, entry);
}
log_debug3("added macro \"%s\": %lld", macro->name,
macro->value.num);
xfree($1);
}
replstrslist: replstrslist strv
{
if (*$2 == '\0')
yyerror("empty string in list");
$$ = $1;
ARRAY_EXPAND($$, 1);
ARRAY_LAST($$).str = $2;
}
| strv
{
if (*$1 == '\0')
yyerror("empty string in list");
$$ = xmalloc(sizeof *$$);
ARRAY_INIT($$);
ARRAY_EXPAND($$, 1);
ARRAY_LAST($$).str = $1;
}
stringslist: stringslist replstrv
{
if (*$2 == '\0')
yyerror("empty string in list");
$$ = $1;
ARRAY_ADD($$, $2);
}
| replstrv
{
if (*$1 == '\0')
yyerror("empty string in list");
$$ = xmalloc(sizeof *$$);
ARRAY_INIT($$);
ARRAY_ADD($$, $1);
}
pathslist: pathslist replpathv
{
if (*$2 == '\0')
yyerror("invalid path");
$$ = $1;
ARRAY_ADD($$, $2);
}
| replpathv
{
if (*$1 == '\0')
yyerror("invalid path");
$$ = xmalloc(sizeof *$$);
ARRAY_INIT($$);
ARRAY_ADD($$, $1);
}
ufn: TOKPASSWD
{
$$ = &passwd_lookup;
}
ufnlist: ufnlist ufn
{
$$ = $1;
ARRAY_ADD($$, $2);
}
| ufn
{
$$ = xmalloc(sizeof *$$);
ARRAY_INIT($$);
ARRAY_ADD($$, $1);
}
rmheaders: rmheaderp strv
{
if (*$2 == '\0')
yyerror("invalid header");
$$ = xmalloc(sizeof *$$);
ARRAY_INIT($$);
ARRAY_EXPAND($$, 1);
ARRAY_LAST($$).str = $2;
}
| rmheaderp '{' replstrslist '}'
{
$$ = $3;
}
maildirs: maildirp replpathv
{
if (*$2 == '\0')
yyerror("invalid path");
$$ = xmalloc(sizeof *$$);
ARRAY_INIT($$);
ARRAY_ADD($$, $2);
}
| maildirp '{' pathslist '}'
{
$$ = $3;
}
mboxes: mboxp replpathv
{
if (*$2 == '\0')
yyerror("invalid path");
$$ = xmalloc(sizeof *$$);
ARRAY_INIT($$);
ARRAY_ADD($$, $2);
}
| mboxp '{' pathslist '}'
{
$$ = $3;
}
folders: folderp replstrv
{
if (*$2 == '\0')
yyerror("invalid folder");
$$ = xmalloc(sizeof *$$);
ARRAY_INIT($$);
ARRAY_ADD($$, $2);
}
| folderp '{' stringslist '}'
{
$$ = $3;
}
lock: TOKFCNTL
{
$$ = LOCK_FCNTL;
}
| TOKFLOCK
{
$$ = LOCK_FLOCK;
}
| TOKDOTLOCK
{
$$ = LOCK_DOTLOCK;
}
locklist: locklist lock
{
$$ = $1 | $2;
}
| lock
{
$$ = $1;
}
| TOKNONE
{
$$ = 0;
}
localgid: replstrv
{
struct group *gr;
if (*$1 == '\0')
yyerror("invalid group");
gr = getgrnam($1);
if (gr == NULL)
yyerror("unknown group: %s", $1);
$$ = gr->gr_gid;
endgrent();
xfree($1);
}
| numv
{
struct group *gr;
if ($1 > GID_MAX)
yyerror("invalid gid: %llu", $1);
gr = getgrgid($1);
if (gr == NULL)
yyerror("unknown gid: %llu", $1);
$$ = gr->gr_gid;
endgrent();
}
user: /* empty */
{
$$ = NULL;
}
| TOKUSER strv
{
$$ = $2;
}
users: /* empty */
{
$$ = NULL;
}
| userp strv
{
$$ = xmalloc(sizeof *$$);
ARRAY_INIT($$);
ARRAY_EXPAND($$, 1);
ARRAY_LAST($$).str = $2;
}
| userp '{' replstrslist '}'
{
$$ = $3;
}
casere: TOKCASE replstrv
{
/* match case */
$$.flags = 0;
$$.str = $2;
}
| replstrv
{
/* ignore case */
$$.flags = RE_IGNCASE;
$$.str = $1;
}
not: TOKNOT
{
$$ = 1;
}
| /* empty */
{
$$ = 0;
}
keep: TOKKEEP
{
$$ = 1;
}
| /* empty */
{
$$ = 0;
}
disabled: TOKDISABLED
{
$$ = 1;
}
| /* empty */
{
$$ = 0;
}
port: TOKPORT replstrv
{
if (*$2 == '\0')
yyerror("invalid port");
$$ = $2;
}
| TOKPORT numv
{
if ($2 == 0 || $2 > 65535)
yyerror("invalid port");
xasprintf(&$$, "%lld", $2);
}
server: TOKSERVER replstrv port
{
if (*$2 == '\0')
yyerror("invalid host");
$$.host = $2;
$$.port = $3;
}
| TOKSERVER replstrv
{
if (*$2 == '\0')
yyerror("invalid host");
$$.host = $2;
$$.port = NULL;
}
to: /* empty */
{
$$ = NULL;
}
| TOKTO strv
{
$$ = $2;
}
from: /* empty */
{
$$ = NULL;
}
| TOKFROM strv
{
$$ = $2;
}
compress: TOKCOMPRESS
{
$$ = 1;
}
| /* empty */
{
$$ = 0;
}
actitem: execpipe strv
{
struct deliver_pipe_data *data;
if (*$2 == '\0')
yyerror("invalid command");
$$ = xcalloc(1, sizeof *$$);
$$->deliver = &deliver_pipe;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->pipe = $1;
data->cmd.str = $2;
}
| TOKREWRITE strv
{
struct deliver_rewrite_data *data;
if (*$2 == '\0')
yyerror("invalid command");
$$ = xcalloc(1, sizeof *$$);
$$->deliver = &deliver_rewrite;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->cmd.str = $2;
}
| writeappend strv
{
struct deliver_write_data *data;
if (*$2 == '\0')
yyerror("invalid path");
$$ = xcalloc(1, sizeof *$$);
$$->deliver = &deliver_write;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->append = $1;
data->path.str = $2;
}
| TOKMAILDIR strv
{
struct deliver_maildir_data *data;
if (*$2 == '\0')
yyerror("invalid path");
$$ = xcalloc(1, sizeof *$$);
$$->deliver = &deliver_maildir;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->path.str = $2;
}
| rmheaders
{
struct deliver_remove_header_data *data;
$$ = xcalloc(1, sizeof *$$);
$$->deliver = &deliver_remove_header;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->hdrs = $1;
}
| TOKADDHEADER strv val
{
struct deliver_add_header_data *data;
if (*$2 == '\0')
yyerror("invalid header");
$$ = xcalloc(1, sizeof *$$);
$$->deliver = &deliver_add_header;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->hdr.str = $2;
data->value.str = $3;
}
| TOKMBOX strv compress
{
struct deliver_mbox_data *data;
if (*$2 == '\0')
yyerror("invalid path");
$$ = xcalloc(1, sizeof *$$);
$$->deliver = &deliver_mbox;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->path.str = $2;
data->compress = $3;
}
| imaptype server userpassnetrc folder1 verify nocrammd5 nologin
starttls insecure
{
struct deliver_imap_data *data;
if ($1 && $8)
yyerror("use either imaps or set starttls");
$$ = xcalloc(1, sizeof *$$);
$$->deliver = &deliver_imap;
data = xcalloc(1, sizeof *data);
$$->data = data;
if ($3.user_netrc && $3.pass_netrc)
find_netrc($2.host, &data->user, &data->pass);
else {
if ($3.user_netrc)
find_netrc($2.host, &data->user, NULL);
else
data->user = $3.user;
if ($3.pass_netrc)
find_netrc($2.host, NULL, &data->pass);
else
data->pass = $3.pass;
}
data->folder.str = $4;
data->server.ssl = $1;
data->server.verify = $5;
data->server.host = $2.host;
if ($2.port != NULL)
data->server.port = $2.port;
else if ($1)
data->server.port = xstrdup("imaps");
else
data->server.port = xstrdup("imap");
data->server.ai = NULL;
data->nocrammd5 = $6;
data->nologin = $7;
data->starttls = $8;
data->server.insecure = $9;
}
| TOKSMTP server from to
{
struct deliver_smtp_data *data;
$$ = xcalloc(1, sizeof *$$);
$$->deliver = &deliver_smtp;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->server.host = $2.host;
if ($2.port != NULL)
data->server.port = $2.port;
else
data->server.port = xstrdup("smtp");
data->server.ai = NULL;
data->from.str = $3;
data->to.str = $4;
}
| TOKSTDOUT
{
$$ = xcalloc(1, sizeof *$$);
$$->deliver = &deliver_stdout;
}
| TOKTAG strv optval
{
struct deliver_tag_data *data;
if (*$2 == '\0')
yyerror("invalid tag");
$$ = xcalloc(1, sizeof *$$);
$$->deliver = &deliver_tag;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->key.str = $2;
data->value.str = $3;
}
| TOKADDTOCACHE replpathv TOKKEY strv
{
struct deliver_add_to_cache_data *data;
if (*$2 == '\0')
yyerror("invalid path");
if (*$4 == '\0')
yyerror("invalid key");
$$ = xcalloc(1, sizeof *$$);
$$->deliver = &deliver_add_to_cache;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->key.str = $4;
data->path = $2;
}
| TOKREMOVEFROMCACHE replpathv TOKKEY strv
{
struct deliver_remove_from_cache_data *data;
if (*$2 == '\0')
yyerror("invalid path");
if (*$4 == '\0')
yyerror("invalid key");
$$ = xcalloc(1, sizeof *$$);
$$->deliver = &deliver_remove_from_cache;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->key.str = $4;
data->path = $2;
}
| actions
{
struct deliver_action_data *data;
/*
* This is a special-case, handled when the list of delivery
* targets is resolved rather than by calling a deliver
* function, so the deliver pointer is NULL.
*/
$$ = xcalloc(1, sizeof *$$);
$$->deliver = NULL;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->actions = $1;
}
| TOKDROP
{
$$ = xcalloc(1, sizeof *$$);
$$->deliver = &deliver_drop;
}
| TOKKEEP
{
$$ = xcalloc(1, sizeof *$$);
$$->deliver = &deliver_keep;
}
actlist: actlist actitem
{
$$ = $1;
TAILQ_INSERT_TAIL($$, $2, entry);
$2->idx = parse_actionidx++;
}
| actitem
{
$$ = xmalloc(sizeof *$$);
TAILQ_INIT($$);
TAILQ_INSERT_HEAD($$, $1, entry);
$1->idx = 0;
parse_actionidx = 1;
}
defaction: TOKACTION replstrv users actitem
{
struct action *t;
if (strlen($2) >= MAXNAMESIZE)
yyerror("action name too long: %s", $2);
if (*$2 == '\0')
yyerror("invalid action name");
if (find_action($2) != NULL)
yyerror("duplicate action: %s", $2);
t = xmalloc(sizeof *t);
strlcpy(t->name, $2, sizeof t->name);
t->list = xmalloc(sizeof *t->list);
TAILQ_INIT(t->list);
TAILQ_INSERT_HEAD(t->list, $4, entry);
$4->idx = 0;
t->users = $3;
TAILQ_INSERT_TAIL(&conf.actions, t, entry);
print_action(t);
xfree($2);
}
| TOKACTION replstrv users '{' actlist '}'
{
struct action *t;
if (strlen($2) >= MAXNAMESIZE)
yyerror("action name too long: %s", $2);
if (*$2 == '\0')
yyerror("invalid action name");
if (find_action($2) != NULL)
yyerror("duplicate action: %s", $2);
t = xmalloc(sizeof *t);
strlcpy(t->name, $2, sizeof t->name);
t->list = $5;
t->users = $3;
TAILQ_INSERT_TAIL(&conf.actions, t, entry);
print_action(t);
xfree($2);
}
accounts: accountp strv
{
if (*$2 == '\0')
yyerror("invalid account name");
if (!have_accounts($2))
yyerror("no matching accounts: %s", $2);
$$ = xmalloc(sizeof *$$);
ARRAY_INIT($$);
ARRAY_EXPAND($$, 1);
ARRAY_LAST($$).str = $2;
}
| accountp '{' replstrslist '}'
{
$$ = $3;
}
actions: actionp strv
{
if (*$2 == '\0')
yyerror("invalid action name");
$$ = xmalloc(sizeof *$$);
ARRAY_INIT($$);
ARRAY_EXPAND($$, 1);
ARRAY_LAST($$).str = $2;
}
| actionp '{' replstrslist '}'
{
$$ = $3;
}
cont: /* empty */
{
$$ = 0;
}
| TOKCONTINUE
{
$$ = 1;
}
area: /* empty */
{
$$ = AREA_ANY;
}
| TOKIN TOKALL
{
$$ = AREA_ANY;
}
| TOKIN TOKHEADERS
{
$$ = AREA_HEADERS;
}
| TOKIN TOKBODY
{
$$ = AREA_BODY;
}
retrc: numv
{
if ($1 < 0 || $1 > 255)
yyerror("invalid return code");
$$ = $1;
}
| /* empty */
{
$$ = -1;
}
retre: casere
{
$$ = $1;
}
| /* empty */
{
$$.str = NULL;
}
ltgt: '<'
{
$$ = CMP_LT;
}
| '>'
{
$$ = CMP_GT;
}
eqne: TOKEQ
{
$$ = CMP_EQ;
}
| TOKNE
{
$$ = CMP_NE;
}
cmp: ltgt
{
$$ = $1;
}
| eqne
{
$$ = $1;
}
execpipe: TOKEXEC
{
$$ = 0;
}
| TOKPIPE
{
$$ = 1;
}
writeappend: TOKWRITE
{
$$ = 0;
}
| TOKAPPEND
{
$$ = 1;
}
exprop: TOKAND
{
$$ = OP_AND;
}
| TOKOR
{
$$ = OP_OR;
}
expritem: not TOKALL
{
$$ = xcalloc(1, sizeof *$$);
$$->match = &match_all;
$$->inverted = $1;
}
| not casere area
{
struct match_regexp_data *data;
char *cause;
$$ = xcalloc(1, sizeof *$$);
$$->match = &match_regexp;
$$->inverted = $1;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->area = $3;
if (re_compile(&data->re, $2.str, $2.flags, &cause) != 0)
yyerror("%s", cause);
xfree($2.str);
}
| not accounts
{
struct match_account_data *data;
$$ = xcalloc(1, sizeof *$$);
$$->match = &match_account;
$$->inverted = $1;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->accounts = $2;
}
| not execpipe strv user TOKRETURNS '(' retrc ',' retre ')'
{
struct match_command_data *data;
char *cause;
if (*$3 == '\0' || ($3[0] == '|' && $3[1] == '\0'))
yyerror("invalid command");
if ($7 == -1 && $9.str == NULL)
yyerror("return code or regexp must be specified");
$$ = xcalloc(1, sizeof *$$);
$$->match = &match_command;
$$->inverted = $1;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->user.str = $4;
data->pipe = $2;
data->cmd.str = $3;
data->ret = $7;
if ($9.str != NULL) {
if (re_compile(
&data->re, $9.str, $9.flags, &cause) != 0)
yyerror("%s", cause);
xfree($9.str);
}
}
| not TOKTAGGED strv
{
struct match_tagged_data *data;
if (*$3 == '\0')
yyerror("invalid tag");
$$ = xcalloc(1, sizeof *$$);
$$->match = &match_tagged;
$$->inverted = $1;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->tag.str = $3;
}
| not TOKSIZE ltgt size
{
struct match_size_data *data;
#if SIZE_MAX < LLONG_MAX
if ($4 > SIZE_MAX)
yyerror("size too large");
#endif
$$ = xcalloc(1, sizeof *$$);
$$->match = &match_size;
$$->inverted = $1;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->size = $4;
data->cmp = $3;
}
| not TOKSTRING strv TOKTO casere
{
struct match_string_data *data;
char *cause;
if (*$3 == '\0')
yyerror("invalid string");
$$ = xcalloc(1, sizeof *$$);
$$->match = &match_string;
$$->inverted = $1;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->str.str = $3;
if (re_compile(
&data->re, $5.str, $5.flags|RE_NOSUBST, &cause) != 0)
yyerror("%s", cause);
xfree($5.str);
}
| not TOKINCACHE replpathv TOKKEY strv
{
struct match_in_cache_data *data;
if (*$3 == '\0')
yyerror("invalid path");
if (*$5 == '\0')
yyerror("invalid key");
$$ = xcalloc(1, sizeof *$$);
$$->match = &match_in_cache;
$$->inverted = $1;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->key.str = $5;
data->path = $3;
}
| not TOKMATCHED
{
$$ = xcalloc(1, sizeof *$$);
$$->match = &match_matched;
$$->inverted = $1;
}
| not TOKUNMATCHED
{
$$ = xcalloc(1, sizeof *$$);
$$->match = &match_unmatched;
$$->inverted = $1;
}
| not TOKAGE ltgt time
{
struct match_age_data *data;
if ($4 == 0)
yyerror("invalid time");
$$ = xcalloc(1, sizeof *$$);
$$->match = &match_age;
$$->inverted = $1;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->time = $4;
data->cmp = $3;
}
| not TOKAGE TOKINVALID
{
struct match_age_data *data;
$$ = xcalloc(1, sizeof *$$);
$$->match = &match_age;
$$->inverted = $1;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->time = -1;
}
| not TOKATTACHMENT TOKCOUNT cmp numv
{
struct match_attachment_data *data;
$$ = xcalloc(1, sizeof *$$);
$$->match = &match_attachment;
$$->inverted = $1;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->op = ATTACHOP_COUNT;
data->cmp = $4;
data->value.num = $5;
}
| not TOKATTACHMENT TOKTOTALSIZE ltgt size
{
struct match_attachment_data *data;
#if SIZE_MAX < LLONG_MAX
if ($5 > SIZE_MAX)
yyerror("size too large");
#endif
$$ = xcalloc(1, sizeof *$$);
$$->match = &match_attachment;
$$->inverted = $1;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->op = ATTACHOP_TOTALSIZE;
data->cmp = $4;
data->value.size = $5;
}
| not TOKATTACHMENT TOKANYSIZE ltgt size
{
struct match_attachment_data *data;
#if SIZE_MAX < LLONG_MAX
if ($5 > SIZE_MAX)
yyerror("size too large");
#endif
$$ = xcalloc(1, sizeof *$$);
$$->match = &match_attachment;
$$->inverted = $1;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->op = ATTACHOP_ANYSIZE;
data->cmp = $4;
data->value.size = $5;
}
| not TOKATTACHMENT TOKANYTYPE strv
{
struct match_attachment_data *data;
if (*$4 == '\0')
yyerror("invalid string");
$$ = xcalloc(1, sizeof *$$);
$$->match = &match_attachment;
$$->inverted = $1;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->op = ATTACHOP_ANYTYPE;
data->value.str.str = $4;
}
| not TOKATTACHMENT TOKANYNAME strv
{
struct match_attachment_data *data;
if (*$4 == '\0')
yyerror("invalid string");
$$ = xcalloc(1, sizeof *$$);
$$->match = &match_attachment;
$$->inverted = $1;
data = xcalloc(1, sizeof *data);
$$->data = data;
data->op = ATTACHOP_ANYNAME;
data->value.str.str = $4;
}
exprlist: exprlist exprop expritem
{
$$ = $1;
$3->op = $2;
TAILQ_INSERT_TAIL($$, $3, entry);
}
| exprop expritem
{
$$ = xmalloc(sizeof *$$);
TAILQ_INIT($$);
$2->op = $1;
TAILQ_INSERT_HEAD($$, $2, entry);
}
expr: expritem
{
$$ = xmalloc(sizeof *$$);
TAILQ_INIT($$);
TAILQ_INSERT_HEAD($$, $1, entry);
}
| expritem exprlist
{
$$ = $2;
TAILQ_INSERT_HEAD($$, $1, entry);
}
perform: users actionp actitem cont
{
struct action *t;
$$ = xcalloc(1, sizeof *$$);
$$->idx = parse_ruleidx++;
$$->actions = NULL;
TAILQ_INIT(&$$->rules);
$$->stop = !$4;
$$->users = $1;
t = $$->lambda = xcalloc(1, sizeof *$$->lambda);
xsnprintf(t->name, sizeof t->name, "<rule %u>", $$->idx);
t->users = NULL;
t->list = xmalloc(sizeof *t->list);
TAILQ_INIT(t->list);
TAILQ_INSERT_HEAD(t->list, $3, entry);
$3->idx = 0;
if (parse_rule == NULL)
TAILQ_INSERT_TAIL(&conf.rules, $$, entry);
else
TAILQ_INSERT_TAIL(&parse_rule->rules, $$, entry);
}
| users actionp '{' actlist '}' cont
{
struct action *t;
$$ = xcalloc(1, sizeof *$$);
$$->idx = parse_ruleidx++;
$$->actions = NULL;
TAILQ_INIT(&$$->rules);
$$->stop = !$6;
$$->users = $1;
t = $$->lambda = xcalloc(1, sizeof *$$->lambda);
xsnprintf(t->name, sizeof t->name, "<rule %u>", $$->idx);
t->users = NULL;
t->list = $4;
if (parse_rule == NULL)
TAILQ_INSERT_TAIL(&conf.rules, $$, entry);
else
TAILQ_INSERT_TAIL(&parse_rule->rules, $$, entry);
}
| users actions cont
{
$$ = xcalloc(1, sizeof *$$);
$$->idx = parse_ruleidx++;
$$->lambda = NULL;
$$->actions = $2;
TAILQ_INIT(&$$->rules);
$$->stop = !$3;
$$->users = $1;
if (parse_rule == NULL)
TAILQ_INSERT_TAIL(&conf.rules, $$, entry);
else
TAILQ_INSERT_TAIL(&parse_rule->rules, $$, entry);
}
| '{'
{
$$ = xcalloc(1, sizeof *$$);
$$->idx = parse_ruleidx++;
$$->lambda = NULL;
$$->actions = NULL;
TAILQ_INIT(&$$->rules);
$$->stop = 0;
$$->users = NULL;
if (parse_rule == NULL)
TAILQ_INSERT_TAIL(&conf.rules, $$, entry);
else
TAILQ_INSERT_TAIL(&parse_rule->rules, $$, entry);
ARRAY_ADD(&parse_rulestack, parse_rule);
parse_rule = $$;
}
close: '}'
{
if (parse_rule == NULL)
yyerror("missing {");
parse_rule = ARRAY_LAST(&parse_rulestack);
ARRAY_TRUNC(&parse_rulestack, 1);
}
rule: TOKMATCH expr perform
{
$3->expr = $2;
print_rule($3);
}
folderlist: /* empty */
{
$$ = xmalloc(sizeof *$$);
ARRAY_INIT($$);
ARRAY_ADD($$, xstrdup("INBOX"));
}
| folders
{
$$ = $1;
}
folder1: /* empty */
{
$$ = xstrdup("INBOX");
}
| folderp strv
{
$$ = $2;
}
groups: groupp replstrv
{
if (*$2 == '\0')
yyerror("invalid group");
$$ = xmalloc(sizeof *$$);
ARRAY_INIT($$);
ARRAY_ADD($$, $2);
}
| groupp '{' stringslist '}'
{
$$ = $3;
}
nocrammd5: TOKNOCRAMMD5
{
$$ = 1;
}
| /* empty */
{
$$ = 0;
}
nologin: TOKNOLOGIN
{
$$ = 1;
}
| /* empty */
{
$$ = 0;
}
starttls: TOKSTARTTLS
{
$$ = 1;
}
| /* empty */
{
$$ = 0;
}
uidl: TOKNOUIDL
{
$$ = 0;
}
| /* empty */
{
$$ = 1;
}
insecure: TOKINSECURE
{
$$ = 1;
}
| /* empty */
{
$$ = 0;
}
verify: TOKNOVERIFY
{
$$ = 0;
}
| /* empty */
{
$$ = 1;
}
apop: TOKNOAPOP
{
$$ = 0;
}
| /* empty */
{
$$ = 1;
}
only: TOKNEWONLY
{
$$ = FETCH_ONLY_NEW;
}
| TOKOLDONLY
{
$$ = FETCH_ONLY_OLD;
}
poptype: TOKPOP3
{
$$ = 0;
}
| TOKPOP3S
{
$$ = 1;
}
imaptype: TOKIMAP
{
$$ = 0;
}
| TOKIMAPS
{
$$ = 1;
}
nntptype: TOKNNTP
{
$$ = 0;
}
| TOKNNTPS
{
$$ = 1;
}
userpassnetrc: TOKUSER replstrv TOKPASS replstrv
{
if (*$2 == '\0')
yyerror("invalid user");
if (*$4 == '\0')
yyerror("invalid pass");
$$.user = $2;
$$.user_netrc = 0;
$$.pass = $4;
$$.pass_netrc = 0;
}
| /* empty */
{
$$.user = NULL;
$$.user_netrc = 1;
$$.pass = NULL;
$$.pass_netrc = 1;
}
| TOKUSER replstrv
{
if (*$2 == '\0')
yyerror("invalid user");
$$.user = $2;
$$.user_netrc = 0;
$$.pass = NULL;
$$.pass_netrc = 1;
}
| TOKPASS replstrv
{
if (*$2 == '\0')
yyerror("invalid pass");
$$.user = NULL;
$$.user_netrc = 1;
$$.pass = $2;
$$.pass_netrc = 0;
}
userpassreqd: TOKUSER replstrv TOKPASS replstrv
{
if (*$2 == '\0')
yyerror("invalid user");
if (*$4 == '\0')
yyerror("invalid pass");
$$.user = $2;
$$.user_netrc = 0;
$$.pass = $4;
$$.pass_netrc = 0;
}
userpass: userpassreqd
{
$$.user = $1.user;
$$.user_netrc = $1.user_netrc;
$$.pass = $1.pass;
$$.pass_netrc = $1.pass_netrc;
}
| /* empty */
{
$$.user = NULL;
$$.user_netrc = 0;
$$.pass = NULL;
$$.pass_netrc = 0;
}
poponly: only TOKCACHE replpathv
{
$$.path = $3;
$$.only = $1;
}
| /* empty */
{
$$.path = NULL;
$$.only = FETCH_ONLY_ALL;
}
imaponly: only
{
$$ = $1;
}
| /* empty */
{
$$ = FETCH_ONLY_ALL;
}
fetchtype: poptype server userpassnetrc poponly apop verify uidl starttls
insecure
{
struct fetch_pop3_data *data;
if ($1 && $8)
yyerror("use either pop3s or set starttls");
$$.fetch = &fetch_pop3;
data = xcalloc(1, sizeof *data);
$$.data = data;
if ($3.user_netrc && $3.pass_netrc)
find_netrc($2.host, &data->user, &data->pass);
else {
if ($3.user_netrc)
find_netrc($2.host, &data->user, NULL);
else
data->user = $3.user;
if ($3.pass_netrc)
find_netrc($2.host, NULL, &data->pass);
else
data->pass = $3.pass;
}
data->server.ssl = $1;
data->server.verify = $6;
data->server.host = $2.host;
if ($2.port != NULL)
data->server.port = $2.port;
else if ($1)
data->server.port = xstrdup("pop3s");
else
data->server.port = xstrdup("pop3");
data->server.ai = NULL;
data->apop = $5;
data->uidl = $7;
data->starttls = $8;
data->server.insecure = $9;
data->path = $4.path;
data->only = $4.only;
}
| TOKPOP3 TOKPIPE replstrv userpassreqd poponly apop
{
struct fetch_pop3_data *data;
$$.fetch = &fetch_pop3pipe;
data = xcalloc(1, sizeof *data);
$$.data = data;
data->user = $4.user;
data->pass = $4.pass;
data->pipecmd = $3;
if (data->pipecmd == NULL || *data->pipecmd == '\0')
yyerror("invalid pipe command");
data->apop = $6;
data->path = $5.path;
data->only = $5.only;
}
| imaptype server userpassnetrc folderlist imaponly verify nocrammd5
nologin starttls insecure
{
struct fetch_imap_data *data;
if ($1 && $9)
yyerror("use either imaps or set starttls");
$$.fetch = &fetch_imap;
data = xcalloc(1, sizeof *data);
$$.data = data;
if ($3.user_netrc && $3.pass_netrc)
find_netrc($2.host, &data->user, &data->pass);
else {
if ($3.user_netrc)
find_netrc($2.host, &data->user, NULL);
else
data->user = $3.user;
if ($3.pass_netrc)
find_netrc($2.host, NULL, &data->pass);
else
data->pass = $3.pass;
}
data->folders = $4;
data->server.ssl = $1;
data->server.verify = $6;
data->server.host = $2.host;
if ($2.port != NULL)
data->server.port = $2.port;
else if ($1)
data->server.port = xstrdup("imaps");
else
data->server.port = xstrdup("imap");
data->server.ai = NULL;
data->only = $5;
data->nocrammd5 = $7;
data->nologin = $8;
data->starttls = $9;
data->server.insecure = $10;
}
| TOKIMAP TOKPIPE replstrv userpass folderlist imaponly
{
struct fetch_imap_data *data;
$$.fetch = &fetch_imappipe;
data = xcalloc(1, sizeof *data);
$$.data = data;
data->user = $4.user;
data->pass = $4.pass;
data->folders = $5;
data->pipecmd = $3;
if (data->pipecmd == NULL || *data->pipecmd == '\0')
yyerror("invalid pipe command");
data->only = $6;
}
| TOKSTDIN
{
$$.fetch = &fetch_stdin;
}
| maildirs
{
struct fetch_maildir_data *data;
$$.fetch = &fetch_maildir;
data = xcalloc(1, sizeof *data);
$$.data = data;
data->maildirs = $1;
}
| mboxes
{
struct fetch_mbox_data *data;
$$.fetch = &fetch_mbox;
data = xcalloc(1, sizeof *data);
$$.data = data;
data->mboxes = $1;
}
| nntptype server userpassnetrc groups TOKCACHE replpathv verify
insecure
{
struct fetch_nntp_data *data;
char *cause;
if (*$6 == '\0')
yyerror("invalid cache");
$$.fetch = &fetch_nntp;
data = xcalloc(1, sizeof *data);
$$.data = data;
if ($3.user_netrc && $3.pass_netrc) {
if (find_netrc1($2.host,
&data->user, &data->pass, &cause) != 0) {
log_debug2("%s", cause);
xfree(cause);
data->user = NULL;
data->pass = NULL;
}
} else {
if ($3.user_netrc)
find_netrc($2.host, &data->user, NULL);
else
data->user = $3.user;
if ($3.pass_netrc)
find_netrc($2.host, NULL, &data->pass);
else
data->pass = $3.pass;
}
data->names = $4;
data->path = $6;
if (data->path == NULL || *data->path == '\0')
yyerror("invalid cache");
data->server.ssl = $1;
data->server.verify = $7;
data->server.insecure = $8;
data->server.host = $2.host;
if ($2.port != NULL)
data->server.port = $2.port;
else if ($1)
data->server.port = xstrdup("nntps");
else
data->server.port = xstrdup("nntp");
data->server.ai = NULL;
}
account: TOKACCOUNT replstrv disabled users fetchtype keep
{
struct account *a;
char *su, desc[DESCBUFSIZE];
if (strlen($2) >= MAXNAMESIZE)
yyerror("account name too long: %s", $2);
if (*$2 == '\0')
yyerror("invalid account name");
if (find_account($2) != NULL)
yyerror("duplicate account: %s", $2);
a = xcalloc(1, sizeof *a);
strlcpy(a->name, $2, sizeof a->name);
a->keep = $6;
a->disabled = $3;
a->users = $4;
a->fetch = $5.fetch;
a->data = $5.data;
TAILQ_INSERT_TAIL(&conf.accounts, a, entry);
if (a->users != NULL)
su = fmt_replstrs(" users=", a->users);
else
su = xstrdup("");
a->fetch->desc(a, desc, sizeof desc);
log_debug2("added account \"%s\":%s fetch=%s", a->name, su,
desc);
xfree(su);
xfree($2);
}
%%
/* Programs */