/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999-2021 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library. If not, see
. */
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#include
typedef int (*address_aget_t) (mu_address_t addr, size_t no, char **buf);
address_aget_t
sieve_get_address_part (mu_sieve_machine_t mach)
{
size_t i;
for (i = 0; i < mach->tagcount; i++)
{
mu_sieve_value_t *t = mu_sieve_get_tag_n (mach, i);
if (strcmp (t->tag, "all") == 0)
return mu_address_aget_email;
else if (strcmp (t->tag, "domain") == 0)
return mu_address_aget_domain;
else if (strcmp (t->tag, "localpart") == 0)
return mu_address_aget_local_part;
}
/* RFC 3028, 2.7.4. Comparisons Against Addresses:
If an optional address-part is omitted, the default is ":all". */
return mu_address_aget_email;
}
/* Structure shared between address and envelope tests */
struct address_closure
{
address_aget_t aget; /* appropriate address_aget_ function */
void *data; /* Either mu_header_t or mu_envelope_t */
mu_address_t addr; /* Obtained address */
};
static int
retrieve_address (void *item, void *data, size_t idx, char **pval)
{
struct address_closure *ap = data;
char *val;
int rc;
if (!ap->addr)
{
rc = mu_header_aget_value ((mu_header_t)ap->data, (char*)item, &val);
if (rc)
return rc;
if (mu_str_skip_class (val, MU_CTYPE_BLANK)[0] == 0)
return MU_ERR_NOENT;
rc = mu_address_create (&ap->addr, val);
free (val);
if (rc)
{
if (rc == MU_ERR_EMPTY_ADDRESS)
rc = MU_ERR_NOENT;
return rc;
}
}
rc = ap->aget (ap->addr, idx+1, pval);
if (rc)
mu_address_destroy (&ap->addr);
return rc;
}
int
sieve_test_address (mu_sieve_machine_t mach)
{
mu_sieve_value_t *h, *v;
mu_header_t header = NULL;
struct address_closure clos;
int rc;
h = mu_sieve_get_arg_untyped (mach, 0);
v = mu_sieve_get_arg_untyped (mach, 1);
mu_message_get_header (mu_sieve_get_message (mach), &header);
clos.data = header;
clos.aget = sieve_get_address_part (mach);
clos.addr = NULL;
rc = mu_sieve_vlist_compare (mach, h, v, retrieve_address, NULL, &clos);
mu_address_destroy (&clos.addr);
return rc;
}
struct header_closure
{
mu_message_t message;
size_t nparts;
size_t part;
mu_header_t header;
size_t index;
};
int
retrieve_header (void *item, void *data, size_t idx, char **pval)
{
struct header_closure *hc = data;
char const *hname;
int rc;
if (idx == 0)
{
rc = mu_message_get_header (hc->message, &hc->header);
if (rc)
return rc;
hc->index = 1;
hc->part = 1;
}
do
{
if (!hc->header)
{
if (hc->part <= hc->nparts)
{
mu_message_t msg;
rc = mu_message_get_part (hc->message, hc->part, &msg);
if (rc)
return rc;
hc->part++;
rc = mu_message_get_header (msg, &hc->header);
if (rc)
return rc;
hc->index = 1;
}
else
return 1;
}
while (!mu_header_sget_field_name (hc->header, hc->index, &hname))
{
int i = hc->index++;
if (mu_c_strcasecmp (hname, (char*)item) == 0)
return mu_header_aget_field_value_unfold (hc->header, i, pval);
}
hc->header = NULL;
}
while (hc->part <= hc->nparts);
return MU_ERR_NOENT;
}
int
sieve_test_header (mu_sieve_machine_t mach)
{
mu_sieve_value_t *h, *v;
int rc;
struct header_closure clos;
h = mu_sieve_get_arg_untyped (mach, 0);
v = mu_sieve_get_arg_untyped (mach, 1);
clos.message = mach->msg;
clos.nparts = 0;
if (mu_sieve_get_tag (mach, "mime", SVT_VOID, NULL))
{
int ismime = 0;
rc = mu_message_is_multipart (mach->msg, &ismime);
if (rc)
mu_diag_funcall (MU_DIAG_ERR, "mu_message_is_multipart",
NULL, rc);
if (ismime)
{
rc = mu_message_get_num_parts (mach->msg, &clos.nparts);
if (rc)
mu_diag_funcall (MU_DIAG_ERR, "mu_message_get_num_parts",
NULL, rc);
}
}
rc = mu_sieve_vlist_compare (mach, h, v, retrieve_header, NULL, &clos);
return rc;
}
int
retrieve_envelope (void *item, void *data, size_t idx, char **pval)
{
struct address_closure *ap = data;
int rc;
if (!ap->addr)
{
const char *buf;
if (mu_c_strcasecmp ((char*)item, "from") != 0)
return MU_ERR_NOENT;
rc = mu_envelope_sget_sender ((mu_envelope_t)ap->data, &buf);
if (rc)
return rc;
rc = mu_address_create (&ap->addr, buf);
if (rc)
return rc;
}
rc = ap->aget (ap->addr, idx+1, pval);
if (rc)
mu_address_destroy (&ap->addr);
return rc;
}
int
sieve_test_envelope (mu_sieve_machine_t mach)
{
mu_sieve_value_t *h, *v;
struct address_closure clos;
int rc;
h = mu_sieve_get_arg_untyped (mach, 0);
v = mu_sieve_get_arg_untyped (mach, 1);
mu_message_get_envelope (mu_sieve_get_message (mach),
(mu_envelope_t*)&clos.data);
clos.aget = sieve_get_address_part (mach);
clos.addr = NULL;
rc = mu_sieve_vlist_compare (mach, h, v, retrieve_envelope, NULL, &clos);
mu_address_destroy (&clos.addr);
return rc;
}
int
sieve_test_size (mu_sieve_machine_t mach)
{
int rc = 1;
size_t size;
size_t arg;
mu_sieve_get_arg (mach, 0, SVT_NUMBER, &arg);
mu_message_size (mu_sieve_get_message (mach), &size);
if (mach->tagcount)
{
mu_sieve_value_t *tag = mu_sieve_get_tag_n (mach, 0);
if (strcmp (tag->tag, "over") == 0)
rc = size > arg;
else if (strcmp (tag->tag, "under") == 0)
rc = size < arg;
else
abort ();
}
else
rc = size == arg;
return rc;
}
int
_test_exists (void *item, void *data)
{
mu_header_t hdr = data;
size_t n;
return mu_header_get_value (hdr, (char*)item, NULL, 0, &n);
}
int
sieve_test_exists (mu_sieve_machine_t mach)
{
mu_header_t header = NULL;
mu_sieve_value_t *val;
mu_message_get_header (mu_sieve_get_message (mach), &header);
val = mu_sieve_get_arg_untyped (mach, 0);
return mu_sieve_vlist_do (mach, val, _test_exists, header) == 0;
}
static mu_sieve_tag_def_t address_part_tags[] = {
{ "localpart", SVT_VOID },
{ "domain", SVT_VOID },
{ "all", SVT_VOID },
{ NULL }
};
mu_sieve_tag_def_t mu_sieve_match_part_tags[] = {
{ "is", SVT_VOID },
{ "contains", SVT_VOID },
{ "matches", SVT_VOID },
{ "regex", SVT_VOID },
{ "count", SVT_STRING },
{ "value", SVT_STRING },
{ "comparator", SVT_STRING },
{ NULL }
};
static mu_sieve_tag_def_t size_tags[] = {
{ "over", SVT_VOID },
{ "under", SVT_VOID },
{ NULL }
};
static mu_sieve_tag_def_t mime_tags[] = {
{ "mime", SVT_VOID },
{ NULL }
};
#define ADDRESS_PART_GROUP \
{ address_part_tags, NULL }
#define MATCH_PART_GROUP \
{ mu_sieve_match_part_tags, mu_sieve_match_part_checker }
#define SIZE_GROUP { size_tags, NULL }
#define MIME_GROUP \
{ mime_tags, NULL }
mu_sieve_tag_group_t address_tag_groups[] = {
ADDRESS_PART_GROUP,
MATCH_PART_GROUP,
{ NULL }
};
mu_sieve_data_type address_req_args[] = {
SVT_STRING_LIST,
SVT_STRING_LIST,
SVT_VOID
};
mu_sieve_tag_group_t size_tag_groups[] = {
SIZE_GROUP,
{ NULL }
};
mu_sieve_data_type size_req_args[] = {
SVT_NUMBER,
SVT_VOID
};
mu_sieve_tag_group_t envelope_tag_groups[] = {
ADDRESS_PART_GROUP,
MATCH_PART_GROUP,
{ NULL }
};
mu_sieve_data_type exists_req_args[] = {
SVT_STRING_LIST,
SVT_VOID
};
mu_sieve_tag_group_t header_tag_groups[] = {
MATCH_PART_GROUP,
MIME_GROUP,
{ NULL }
};
void
mu_i_sv_register_standard_tests (mu_sieve_machine_t mach)
{
/* true and false are built-ins */
mu_sieve_register_test (mach, "address", sieve_test_address,
address_req_args, address_tag_groups, 1);
mu_sieve_register_test (mach, "size", sieve_test_size,
size_req_args, size_tag_groups, 1);
mu_sieve_register_test (mach, "envelope", sieve_test_envelope,
address_req_args, envelope_tag_groups, 1);
mu_sieve_register_test (mach, "exists", sieve_test_exists,
exists_req_args, NULL, 1);
mu_sieve_register_test (mach, "header", sieve_test_header,
address_req_args, header_tag_groups, 1);
}