%{
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2003-2021 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GNU Mailutils is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Mailutils. If not, see . */
#include
#include
#include
static node_t *pick_node_create (node_type type, void *a, void *b);
static void set_cflags (char const *str);
static regex_t *
regex_dup (regex_t *re)
{
regex_t *rp = mu_alloc (sizeof (*rp));
*rp = *re;
return rp;
}
int yyerror (const char *s);
int yylex (void);
static node_t *parse_tree;
static int nesting_level;
static int reg_flags = REG_EXTENDED|REG_ICASE;
%}
%token T_COMP T_DATEFIELD T_STRING T_CFLAGS
%token T_LBRACE T_RBRACE T_BEFORE T_AFTER
%left T_OR
%left T_AND
%left T_NOT
%union {
char *string;
node_t *node;
regex_t regex;
};
%type expr exprlist
%type regex
%%
input : /* empty */
{
parse_tree = NULL;
}
| exprlist
{
parse_tree = $1;
}
;
exprlist : expr
| exprlist expr
{
$$ = pick_node_create (node_and, $1, $2);
}
;
cflags : /* empty */
| T_CFLAGS
{
set_cflags ($1);
}
;
regex : cflags T_STRING
{
int rc = regcomp (&$$, $2, reg_flags|REG_NOSUB);
if (rc)
{
char errbuf[512];
regerror (rc, &$$, errbuf, sizeof (errbuf));
mu_error ("error compiling regex \"%s\": %s",
$2, errbuf);
YYERROR;
}
}
;
expr : lbrace exprlist rbrace
{
$$ = $2;
}
| cflags T_COMP regex
{
$$ = pick_node_create (node_regex, $2, regex_dup (&$3));
}
| regex
{
$$ = pick_node_create (node_regex, NULL, regex_dup (&$1));
}
| T_DATEFIELD
{
$$ = pick_node_create (node_datefield, $1, NULL);
}
| T_BEFORE T_STRING
{
time_t t;
if (mu_parse_date ($2, &t, NULL))
{
mu_error (_("bad date format: %s"), $2);
exit (1);
}
$$ = pick_node_create (node_before, NULL, NULL);
$$->v.time = t;
}
| T_AFTER T_STRING
{
time_t t;
if (mu_parse_date ($2, &t, NULL))
{
mu_error (_("bad date format: %s"), $2);
exit (1);
}
$$ = pick_node_create (node_after, NULL, NULL);
$$->v.time = t;
}
| expr T_AND expr
{
$$ = pick_node_create (node_and, $1, $3);
}
| expr T_OR expr
{
$$ = pick_node_create (node_or, $1, $3);
}
| T_NOT expr
{
$$ = pick_node_create (node_not, $2, NULL);
}
;
lbrace : T_LBRACE
{
nesting_level++;
}
;
rbrace : T_RBRACE
{
nesting_level--;
}
;
%%
/* Lexical analizer */
struct token
{
int tok;
char *val;
};
static mu_iterator_t iterator;
int
yylex ()
{
struct token *tok;
if (mu_iterator_is_done (iterator))
return 0;
mu_iterator_current (iterator, (void **)&tok);
mu_iterator_next (iterator);
yylval.string = tok->val;
return tok->tok;
}
static char *
tokname (int tok)
{
switch (tok)
{
case T_DATEFIELD:
return "-datefield";
case T_BEFORE:
return "-before";
case T_AFTER:
return "-after";
case T_LBRACE:
return "-lbrace";
case T_RBRACE:
return "-rbrace";
case T_OR:
return "-or";
case T_AND:
return "-and";
case T_NOT:
return "-not";
}
return NULL;
}
int
yyerror (const char *s)
{
int tok = yylex ();
const char *str;
if (!tok)
str = _("end of input");
else if (yylval.string)
str = yylval.string;
else
str = tokname (tok);
if (nesting_level)
mu_error (_("%s near %s (missing closing brace?)"), s, str);
else
mu_error (_("%s near %s"), s, str);
return 0;
}
void
pick_add_token (mu_list_t *list, int tok, char const *val)
{
struct token *tp;
int rc;
if (!*list && (rc = mu_list_create (list)))
{
mu_error(_("cannot create list: %s"), mu_strerror (rc));
exit (1);
}
tp = mu_alloc (sizeof (*tp));
tp->tok = tok;
tp->val = (char*) val;
mu_list_append (*list, tp);
}
/* Main entry point */
int
pick_parse (mu_list_t toklist)
{
int rc;
if (!toklist)
{
parse_tree = NULL;
return 0;
}
if (mu_list_get_iterator (toklist, &iterator))
return -1;
mu_iterator_first (iterator);
rc = yyparse ();
mu_iterator_destroy (&iterator);
return rc;
}
/* Parse tree functions */
node_t *
pick_node_create (node_type type, void *a, void *b)
{
node_t *node;
node = mu_alloc (sizeof (*node));
node->type = type;
node->v.gen.a = a;
node->v.gen.b = b;
return node;
}
struct eval_env
{
mu_message_t msg;
char *datefield;
};
static int
match_header (mu_message_t msg, char *comp, regex_t *regex)
{
int rc;
size_t i, count;
mu_header_t hdr = NULL;
const char *buf;
rc = mu_message_get_header (msg, &hdr);
if (rc)
{
mu_error (_("cannot get header: %s"), mu_strerror (rc));
return 0;
}
mu_header_get_field_count (hdr, &count);
for (i = 1; i <= count; i++)
{
mu_header_sget_field_name (hdr, i, &buf);
if (mu_c_strcasecmp (buf, comp) == 0)
{
mu_header_sget_field_value (hdr, i, &buf);
if (regexec (regex, buf, 0, NULL, 0) == 0)
return 1;
}
}
return 0;
}
static int
match_message (mu_message_t msg, regex_t *regex)
{
mu_stream_t str = NULL;
char buf[128];
size_t n;
mu_message_get_streamref (msg, &str);
while (mu_stream_readline (str, buf, sizeof buf, &n) == 0
&& n > 0)
{
buf[n] = 0;
if (regexec (regex, buf, 0, NULL, 0) == 0)
return 1;
}
mu_stream_destroy (&str);
return 0;
}
static int
get_date_field (struct eval_env *env, time_t *t)
{
mu_header_t hdr;
char buf[128];
if (mu_message_get_header (env->msg, &hdr))
return 1;
if (mu_header_get_value (hdr, env->datefield, buf, sizeof buf, NULL))
return 1;
return mu_parse_date (buf, t, NULL);
}
static int
pick_eval_node (node_t *node, struct eval_env *env)
{
time_t t;
switch (node->type)
{
case node_and:
if (!pick_eval_node (node->v.op.larg, env))
return 0;
return pick_eval_node (node->v.op.rarg, env);
case node_or:
if (pick_eval_node (node->v.op.larg, env))
return 1;
return pick_eval_node (node->v.op.rarg, env);
case node_not:
return !pick_eval_node (node->v.op.larg, env);
case node_regex:
if (node->v.re.comp)
return match_header (env->msg, node->v.re.comp, node->v.re.regex);
else
return match_message (env->msg, node->v.re.regex);
case node_datefield:
env->datefield = node->v.df.datefield;
return 1;
case node_before:
if (get_date_field (env, &t))
break;
return t < node->v.time;
case node_after:
if (get_date_field (env, &t))
break;
return t > node->v.time;
}
return 0;
}
int
pick_eval (mu_message_t msg)
{
struct eval_env env;
if (!parse_tree)
return 1;
env.msg = msg;
env.datefield = "date";
return pick_eval_node (parse_tree, &env);
}
void
set_cflags (char const *str)
{
reg_flags = 0;
for (; *str; str++)
{
switch (*str)
{
case 'b':
case 'B':
reg_flags &= ~REG_EXTENDED;
break;
case 'e':
case 'E':
reg_flags |= REG_EXTENDED;
break;
case 'c':
case 'C':
reg_flags &= ~REG_ICASE;
break;
case 'i':
case 'I':
reg_flags |= REG_ICASE;
break;
default:
mu_error (_("Invalid regular expression flag: %c"), *str);
exit (1);
}
}
}