mpick: add header decodeop for better address matching

pull/174/head
Duncaen 5 years ago committed by Leah Neukirchen
parent 32f0fcbc0d
commit 8ae4801dd4

@ -103,6 +103,7 @@ tests are given by the following EBNF:
| <flagprop> | <flagprop>
| <timeprop> <numop> <dur> | <timeprop> <numop> <dur>
| <numprop> <numop> <num> | <numprop> <numop> <num>
| <hdrprop> <decodeop> <strop> <str>
| <strprop> <strop> <str> | <strprop> <strop> <str>
| prune -- do not match further messages in thread | prune -- do not match further messages in thread
| print -- always true value | print -- always true value
@ -139,8 +140,13 @@ tests are given by the following EBNF:
| T )? -- *1024*1024*1024*1024 | T )? -- *1024*1024*1024*1024
| cur -- index of cur message | cur -- index of cur message
<strprop> ::= from | subject | to <hdrprop> ::= from | to | subject | <str>
| <str> -- header name
<decodeop> ::= . addr -- match address parts
| . disp -- match address display parts
| -- empty matches raw headers
<strprop> ::= path
<strop> ::= == | = | != -- string (in)equality <strop> ::= == | = | != -- string (in)equality
| === | !=== -- case insensitive string (in)equality | === | !=== -- case insensitive string (in)equality

@ -80,11 +80,12 @@ enum prop {
PROP_REPLIES, PROP_REPLIES,
PROP_SIZE, PROP_SIZE,
PROP_TOTAL, PROP_TOTAL,
PROP_FROM,
PROP_TO,
PROP_INDEX, PROP_INDEX,
PROP_DATE, PROP_DATE,
PROP_FLAG, PROP_FLAG,
PROP_HDR,
PROP_HDR_ADDR,
PROP_HDR_DISP,
}; };
enum flags { enum flags {
@ -332,16 +333,17 @@ freeexpr(struct expr *e)
case EXPR_REGEX: case EXPR_REGEX:
case EXPR_REGEXI: case EXPR_REGEXI:
switch (e->a.prop) { switch (e->a.prop) {
case PROP_PATH: case PROP_PATH: break;
case PROP_FROM: case PROP_HDR:
case PROP_TO: case PROP_HDR_ADDR:
break; case PROP_HDR_DISP:
default: free(e->a.string); free(e->b.string);
default: return;
} }
if (e->op == EXPR_REGEX || e->op == EXPR_REGEXI) if (e->op == EXPR_REGEX || e->op == EXPR_REGEXI)
regfree(e->b.regex); regfree(e->c.regex);
else else
free(e->b.string); free(e->c.string);
} }
free(e); free(e);
} }
@ -579,14 +581,25 @@ parse_strcmp()
negate = 0; negate = 0;
if (token("from")) if (token("from"))
prop = PROP_FROM; h = xstrdup("from");
else if (token("to")) else if (token("to"))
prop = PROP_TO; h = xstrdup("to");
else if (token("subject")) else if (token("subject"))
h = "subject"; h = xstrdup("subject");
else if (token("path"))
prop = PROP_PATH;
else if (!parse_string(&h)) else if (!parse_string(&h))
return parse_inner(); return parse_inner();
if (!prop) {
if (token(".")) {
if (token("addr")) prop = PROP_HDR_ADDR;
else if (token("disp")) prop = PROP_HDR_DISP;
else parse_error_at(NULL, "unknown decode parameter");
} else
prop = PROP_HDR;
}
if (token("~~~")) if (token("~~~"))
op = EXPR_GLOBI; op = EXPR_GLOBI;
else if (token("~~")) else if (token("~~"))
@ -624,35 +637,29 @@ parse_strcmp()
int r = 0; int r = 0;
struct expr *e = mkexpr(op); struct expr *e = mkexpr(op);
e->a.prop = prop;
if (prop) switch (prop) {
e->a.prop = prop; case PROP_HDR:
else case PROP_HDR_ADDR:
e->a.string = h; case PROP_HDR_DISP:
e->b.string = h;
if (prop == PROP_FROM || prop == PROP_TO) { break;
char *disp, *addr; case PROP_PATH: break;
blaze822_addr(s, &disp, &addr);
if (!disp && !addr)
parse_error_at(NULL, "invalid address");
free(s);
s = xstrdup((disp) ? disp : addr);
e->extra = (disp) ? 0 : 1;
} }
if (op == EXPR_REGEX || op == EXPR_REGEXI) { if (op == EXPR_REGEX || op == EXPR_REGEXI) {
e->b.regex = malloc(sizeof (regex_t)); e->c.regex = malloc(sizeof (regex_t));
r = regcomp(e->b.regex, s, REG_EXTENDED | REG_NOSUB | r = regcomp(e->c.regex, s, REG_EXTENDED | REG_NOSUB |
(op == EXPR_REGEXI ? REG_ICASE : 0)); (op == EXPR_REGEXI ? REG_ICASE : 0));
if (r != 0) { if (r != 0) {
char msg[256]; char msg[256];
regerror(r, e->b.regex, msg, sizeof msg); regerror(r, e->c.regex, msg, sizeof msg);
parse_error("invalid regex '%s': %s", s, msg); parse_error("invalid regex '%s': %s", s, msg);
exit(2); exit(2);
} }
free(s); free(s);
} else { } else {
e->b.string = s; e->c.string = s;
} }
if (negate) { if (negate) {
@ -996,12 +1003,13 @@ parse_msglist(char *s)
case '/': case '/':
s++; s++;
e1 = mkexpr(EXPR_REGEXI); e1 = mkexpr(EXPR_REGEXI);
e1->a.string = xstrdup("subject"); e1->a.prop = PROP_HDR;
e1->b.regex = malloc(sizeof (regex_t)); e1->b.string = xstrdup("subject");
r = regcomp(e1->b.regex, s, REG_EXTENDED | REG_NOSUB | REG_ICASE); e1->c.regex = malloc(sizeof (regex_t));
r = regcomp(e1->c.regex, s, REG_EXTENDED | REG_NOSUB | REG_ICASE);
if (r != 0) { if (r != 0) {
char msg[256]; char msg[256];
regerror(r, e1->b.regex, msg, sizeof msg); regerror(r, e1->c.regex, msg, sizeof msg);
parse_error("invalid regex '%s': %s", s, msg); parse_error("invalid regex '%s': %s", s, msg);
} }
return e1; return e1;
@ -1067,14 +1075,14 @@ parse_msglist(char *s)
d = (disp) ? disp : addr; d = (disp) ? disp : addr;
e1 = mkexpr(EXPR_REGEXI); e1 = mkexpr(EXPR_REGEXI);
e1->a.prop = PROP_FROM; e1->a.prop = (disp) ? PROP_HDR_DISP : PROP_HDR_ADDR;
e1->b.regex = malloc(sizeof (regex_t)); e1->b.string = xstrdup("from");
e1->extra = (disp) ? 0 : 1; e1->c.regex = malloc(sizeof (regex_t));
r = regcomp(e1->b.regex, d, REG_EXTENDED | REG_NOSUB | REG_ICASE); r = regcomp(e1->c.regex, d, REG_EXTENDED | REG_NOSUB | REG_ICASE);
if (r != 0) { if (r != 0) {
char msg[256]; char msg[256];
regerror(r, e1->b.regex, msg, sizeof msg); regerror(r, e1->c.regex, msg, sizeof msg);
parse_error("invalid regex '%s': %s", d, msg); parse_error("invalid regex '%s': %s", d, msg);
} }
@ -1106,54 +1114,64 @@ msg_date(struct mailinfo *m)
} }
char * char *
msg_hdr(struct mailinfo *m, const char *h) msg_hdr(char **s, const char *h, struct mailinfo *m)
{ {
static char hdrbuf[4096]; static char hdrbuf[4096];
if (!m->msg && m->fpath) { if (!m->msg && m->fpath) {
if (!(m->msg = blaze822(m->fpath))) { if (!(m->msg = blaze822(m->fpath))) {
m->fpath = NULL; m->fpath = NULL;
*hdrbuf = 0; return NULL;
return hdrbuf;
} }
} }
// XXX: only return one header for now
if (*s)
return NULL;
char *b; char *b;
if (!m->msg || !(b = blaze822_chdr(m->msg, h))) { if (!m->msg || !(b = blaze822_chdr(m->msg, h)))
*hdrbuf = 0; return NULL;
return hdrbuf; *s = b;
}
blaze822_decode_rfc2047(hdrbuf, b, sizeof hdrbuf - 1, "UTF-8"); blaze822_decode_rfc2047(hdrbuf, b, sizeof hdrbuf - 1, "UTF-8");
return hdrbuf; return hdrbuf;
} }
char * char *
msg_addr(struct mailinfo *m, char *h, int t) msg_hdr_addr(char **s, const char *h, struct mailinfo *m, int rdisp)
{ {
if (!m->msg && m->fpath) { if (!m->msg && m->fpath) {
if (!(m->msg = blaze822(m->fpath))) { if (!(m->msg = blaze822(m->fpath))) {
m->fpath = NULL; m->fpath = NULL;
return ""; return NULL;
} }
} }
char *b; char *b = *s;
if (m->msg == 0 || (b = blaze822_chdr(m->msg, h)) == 0) if (!b) {
return ""; if (!m->msg || !(b = blaze822_chdr(m->msg, h)))
return NULL;
}
char *disp, *addr; char *disp, *addr;
blaze822_addr(b, &disp, &addr); *s = blaze822_addr(b, &disp, &addr);
if (t) { if (rdisp)
if (!addr)
return "";
return addr;
} else {
if (!disp)
return "";
return disp; return disp;
} return addr;
}
char *
msg_hdr_address(char **s, const char *h, struct mailinfo *m)
{
return msg_hdr_addr(s, h, m, 0);
}
char *
msg_hdr_display(char **s, const char *h, struct mailinfo *m)
{
return msg_hdr_addr(s, h, m, 1);
} }
FILE * FILE *
@ -1283,21 +1301,34 @@ eval(struct expr *e, struct mailinfo *m)
case EXPR_GLOBI: case EXPR_GLOBI:
case EXPR_REGEX: case EXPR_REGEX:
case EXPR_REGEXI: { case EXPR_REGEXI: {
const char *s; const char *s = NULL;
char *p = NULL;
char *(*fn)(char **, const char *, struct mailinfo *) = 0;
int rv = 0;
switch (e->a.prop) { switch (e->a.prop) {
case PROP_HDR: fn = msg_hdr; break;
case PROP_HDR_ADDR: fn = msg_hdr_address; break;
case PROP_HDR_DISP: fn = msg_hdr_display; break;
case PROP_PATH: s = m->fpath ? m->fpath : ""; break; case PROP_PATH: s = m->fpath ? m->fpath : ""; break;
case PROP_FROM: s = msg_addr(m, "from", e->extra); break; default: return 0;
case PROP_TO: s = msg_addr(m, "to", e->extra); break;
default: s = msg_hdr(m, e->a.string); break;
} }
switch (e->op) { for (;;) {
case EXPR_STREQ: return strcmp(e->b.string, s) == 0; if (fn && !(s = fn(&p, e->b.string, m)))
case EXPR_STREQI: return strcasecmp(e->b.string, s) == 0; break;
case EXPR_GLOB: return fnmatch(e->b.string, s, 0) == 0; switch (e->op) {
case EXPR_GLOBI: return fnmatch(e->b.string, s, FNM_CASEFOLD) == 0; case EXPR_STREQ: rv = strcmp(e->c.string, s) == 0; break;
case EXPR_REGEX: case EXPR_STREQI: rv = strcasecmp(e->c.string, s) == 0; break;
case EXPR_REGEXI: return regexec(e->b.regex, s, 0, 0, 0) == 0; case EXPR_GLOB: rv = fnmatch(e->c.string, s, 0) == 0; break;
case EXPR_GLOBI:
rv = fnmatch(e->c.string, s, FNM_CASEFOLD) == 0; break;
case EXPR_REGEX:
case EXPR_REGEXI:
rv = regexec(e->c.regex, s, 0, 0, 0) == 0;
break;
}
if (!fn || rv) return rv;
} }
return 0;
} }
} }
return 0; return 0;
@ -1504,8 +1535,9 @@ main(int argc, char *argv[])
if (optind != argc) { if (optind != argc) {
for (c = optind; c < argc; c++) { for (c = optind; c < argc; c++) {
if (strchr(argv[c], '/') && access(argv[c], R_OK) == 0) if (strchr(argv[c], '/') && access(argv[c], R_OK) == 0) {
break; break;
}
expr = chain(expr, EXPR_AND, parse_msglist(argv[c])); expr = chain(expr, EXPR_AND, parse_msglist(argv[c]));
} }

@ -1,7 +1,7 @@
#!/bin/sh -e #!/bin/sh -e
cd ${0%/*} cd ${0%/*}
. ./lib.sh . ./lib.sh
plan 13 plan 15
rm -rf test.dir rm -rf test.dir
mkdir test.dir mkdir test.dir
@ -66,5 +66,7 @@ check_test 'search addr' -eq 2 'mlist inbox | mpick peter@example.org | wc -l'
check_test 'search name' -eq 2 'mlist inbox | mpick "Peter Example" | wc -l' check_test 'search name' -eq 2 'mlist inbox | mpick "Peter Example" | wc -l'
check_test 'search spam' -eq 1 'mlist inbox | mpick -t "trashed && subject =~ \"pdf\"" | wc -l' check_test 'search spam' -eq 1 'mlist inbox | mpick -t "trashed && subject =~ \"pdf\"" | wc -l'
check_test 'any header' -eq 1 'mlist inbox | mpick -t "\"Foo\" =~~ \"bar\"" | wc -l' check_test 'any header' -eq 1 'mlist inbox | mpick -t "\"Foo\" =~~ \"bar\"" | wc -l'
check_test 'addr decode addr' -eq 2 'mlist inbox | mpick -t "from.addr == \"peter@example.org\"" | wc -l'
check_test 'addr decode disp' -eq 2 'mlist inbox | mpick -t "from.disp == \"Peter Example\"" | wc -l'
) )

Loading…
Cancel
Save