Commit 6fad8e17 6fad8e1734a83c273f6283436775a6548f9c941f by Sergey Poznyakoff

Implement editheader sieve extension (RFC 5293).

Also fix iterator synchronization after removing an element and
improve Sieve API.

* include/mailutils/header.h (mu_header_get_itemptr): New proto.
* include/mailutils/iterator.h (mu_iterator_advance): Remove.
(mu_iterator_delitem): New proto.
(mu_iterator_set_delitem): New proto.
* include/mailutils/sieve.h (mu_sieve_register_t) <opt_args>: New member.
(mu_sieve_register_test_ext)
(mu_sieve_register_action_ext): New protos.
* include/mailutils/sys/iterator.h (_mu_iterator) <curitem_p>: Remove.
<delitem>: New member. All uses updated.

* libmailutils/base/iterator.c (mu_iterator_set_delitem): New function.

* libmailutils/mailbox/hdritr.c: Implement bidirectional iteration.
Implement itrctl method.
* libmailutils/mailbox/header.c: Likewise.

* libmailutils/base/assoc.c: Use delitem method instead of curitem_p.
(mu_iterator_delitem): New function.
* libmailutils/base/opool.c
* libmailutils/diag/debug.c
* libmailutils/list/iterator.c
* libmailutils/list/pop.c
* libmailutils/list/remove.c
* libmailutils/list/removenth.c
* libmailutils/mailbox/imapenv.c
* libmailutils/mailbox/mbxitr.c
* libproto/pop/pop3_iterator.c

* libmu_sieve/extensions/Makefile.am: Add editheader.c
* libmu_sieve/extensions/editheader.c: New file.

* libmu_sieve/prog.c (mu_sv_code_command): Allow for optional positional
arguments.
* libmu_sieve/register.c (mu_sieve_test_lookup)
(mu_sieve_action_lookup): Return NULL if a record with
empty (NULL) handler is found.
(mu_sieve_register_test_ext)
(mu_sieve_register_action_ext): New functions.
(mu_sieve_register_test)
(mu_sieve_register_action): Rewrite as wrappers to the above.
* libmu_sieve/util.c (mu_sieve_vlist_do): Allow for SVT_STRING
argument.
* sieve/tests/Makefile.am: Add new testcases.
* sieve/tests/testsuite.at: Include new testcases.
* sieve/tests/addheader.at: New testcase.
* sieve/tests/delheader.at: New testcase.

* NEWS: Update.
* doc/rfc/README: Update.
1 parent a7a174d5
GNU mailutils NEWS -- history of user-visible changes. 2012-08-07
GNU mailutils NEWS -- history of user-visible changes. 2012-11-12
Copyright (C) 2002-2012 Free Software Foundation, Inc.
See the end of file for copying conditions.
......@@ -114,6 +114,10 @@ header field with the given date.
See <http://mailutils.org/wiki/Timestamp_(Sieve_test)>.
Implemented the Editheader extension: `addheader' and `delheader' actions.
See <http://tools.ietf.org/html/rfc5293>.
** mail: sending attachments
The mail[x] utility now allows for sending attachments. Any number of
......
......@@ -53,3 +53,4 @@ the actual RFC number.
3501 INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1
3691 Internet Message Access Protocol (IMAP) UNSELECT command
4314 IMAP4 Access Control List (ACL) Extension
5293 Sieve Email Filtering: Editheader Extension
......
......@@ -169,6 +169,8 @@ extern int mu_header_set_fill (mu_header_t,
int (*_fill) (void *data, char **, size_t *),
void *data);
extern int mu_header_get_itemptr (mu_header_t header, size_t num,
const void **sptr);
#ifdef __cplusplus
}
......
......@@ -37,7 +37,7 @@ enum mu_itrctl_req
mu_itrctl_qry_direction, /* Query iteration direction */
mu_itrctl_set_direction /* Set iteration direction */
};
extern int mu_iterator_create (mu_iterator_t *, void *);
extern int mu_iterator_dup (mu_iterator_t *piterator, mu_iterator_t orig);
extern void mu_iterator_destroy (mu_iterator_t *);
......@@ -53,7 +53,7 @@ extern int mu_iterator_ctl (mu_iterator_t, enum mu_itrctl_req, void *);
extern int mu_iterator_attach (mu_iterator_t *root, mu_iterator_t iterator);
extern int mu_iterator_detach (mu_iterator_t *root, mu_iterator_t iterator);
extern void mu_iterator_advance (mu_iterator_t iterator, void *e);
extern void mu_iterator_delitem (mu_iterator_t iterator, void *e);
extern int mu_iterator_set_first (mu_iterator_t, int (*first) (void *));
extern int mu_iterator_set_next (mu_iterator_t, int (*next) (void *));
......@@ -66,8 +66,13 @@ extern int mu_iterator_set_dup (mu_iterator_t itr,
int (*dup) (void **ptr, void *data));
extern int mu_iterator_set_destroy (mu_iterator_t itr,
int (*destroy) (mu_iterator_t, void *data));
extern int mu_iterator_set_curitem_p (mu_iterator_t itr,
int (*curitem_p) (void *, void *));
#define MU_ITR_DELITEM_NOTHING 0
#define MU_ITR_DELITEM_NEXT 1
#define MU_ITR_DELITEM_ADVANCE 2
extern int mu_iterator_set_delitem (mu_iterator_t itr,
int (*delitem) (void *, void *));
extern int mu_iterator_set_itrctl (mu_iterator_t itr,
int (*itrctl) (void *,
enum mu_itrctl_req,
......
......@@ -100,6 +100,7 @@ typedef struct
int required;
mu_sieve_handler_t handler;
mu_sieve_data_type *req_args;
mu_sieve_data_type *opt_args;
mu_sieve_tag_group_t *tags;
} mu_sieve_register_t;
......@@ -146,10 +147,21 @@ mu_sieve_register_t *mu_sieve_test_lookup (mu_sieve_machine_t mach,
const char *name);
mu_sieve_register_t *mu_sieve_action_lookup (mu_sieve_machine_t mach,
const char *name);
int mu_sieve_register_test_ext (mu_sieve_machine_t mach,
const char *name, mu_sieve_handler_t handler,
mu_sieve_data_type *req_args,
mu_sieve_data_type *opt_args,
mu_sieve_tag_group_t *tags, int required);
int mu_sieve_register_test (mu_sieve_machine_t mach,
const char *name, mu_sieve_handler_t handler,
mu_sieve_data_type * arg_types,
mu_sieve_tag_group_t * tags, int required);
int mu_sieve_register_action_ext (mu_sieve_machine_t mach,
const char *name, mu_sieve_handler_t handler,
mu_sieve_data_type *req_args,
mu_sieve_data_type *opt_args,
mu_sieve_tag_group_t *tags, int required);
int mu_sieve_register_action (mu_sieve_machine_t mach,
const char *name, mu_sieve_handler_t handler,
mu_sieve_data_type * arg_types,
......
......@@ -36,7 +36,7 @@ struct _mu_iterator
int (*first) (void *owner);
int (*next) (void *owner);
int (*getitem) (void *owner, void **pret, const void **pkey);
int (*curitem_p) (void *owner, void *item);
int (*delitem) (void *owner, void *item);
int (*finished_p) (void *owner);
int (*itrctl) (void *owner, enum mu_itrctl_req req, void *arg);
void *(*dataptr) (void *);
......
......@@ -452,13 +452,13 @@ destroy (mu_iterator_t iterator, void *data)
}
static int
curitem_p (void *owner, void *item)
delitem (void *owner, void *item)
{
struct assoc_iterator *itr = owner;
mu_assoc_t assoc = itr->assoc;
struct _mu_assoc_elem *elem = ASSOC_ELEM (assoc, itr->index);
return elem == item;
return elem == item ? MU_ITR_DELITEM_NEXT : MU_ITR_DELITEM_NOTHING;
}
static int
......@@ -498,7 +498,7 @@ mu_assoc_get_iterator (mu_assoc_t assoc, mu_iterator_t *piterator)
mu_iterator_set_next (iterator, next);
mu_iterator_set_getitem (iterator, getitem);
mu_iterator_set_finished_p (iterator, finished_p);
mu_iterator_set_curitem_p (iterator, curitem_p);
mu_iterator_set_delitem (iterator, delitem);
mu_iterator_set_destroy (iterator, destroy);
mu_iterator_set_dup (iterator, assoc_data_dup);
......
......@@ -81,12 +81,12 @@ mu_iterator_set_finished_p (mu_iterator_t itr, int (*finished_p) (void *))
}
int
mu_iterator_set_curitem_p (mu_iterator_t itr,
int (*curitem_p) (void *, void *))
mu_iterator_set_delitem (mu_iterator_t itr,
int (*delitem) (void *, void *))
{
if (!itr)
return EINVAL;
itr->curitem_p = curitem_p;
itr->delitem = delitem;
return 0;
}
......@@ -158,7 +158,7 @@ mu_iterator_dup (mu_iterator_t *piterator, mu_iterator_t orig)
iterator->first = orig->first;
iterator->next = orig->next;
iterator->getitem = orig->getitem;
iterator->curitem_p = orig->curitem_p;
iterator->delitem = orig->delitem;
iterator->finished_p = orig->finished_p;
iterator->itrctl = orig->itrctl;
......@@ -250,14 +250,19 @@ iterator_get_owner (mu_iterator_t iterator, void **powner)
}
void
mu_iterator_advance (mu_iterator_t iterator, void *e)
mu_iterator_delitem (mu_iterator_t iterator, void *itm)
{
for (; iterator; iterator = iterator->next_itr)
{
if (iterator->curitem_p (iterator->owner, e))
if (iterator->delitem)
{
iterator->next (iterator->owner);
iterator->is_advanced++;
switch (iterator->delitem (iterator->owner, itm))
{
case MU_ITR_DELITEM_NEXT:
iterator->next (iterator->owner);
case MU_ITR_DELITEM_ADVANCE:
iterator->is_advanced++;
}
}
}
}
......
......@@ -384,10 +384,11 @@ opitr_finished_p (void *owner)
}
static int
opitr_curitem_p (void *owner, void *item)
opitr_delitem (void *owner, void *item)
{
struct opool_iterator *itr = owner;
return itr->cur && itr->cur->buf == item;
return (itr->cur && itr->cur->buf == item) ?
MU_ITR_DELITEM_NEXT : MU_ITR_DELITEM_NOTHING;
}
static int
......@@ -446,7 +447,7 @@ mu_opool_get_iterator (mu_opool_t opool, mu_iterator_t *piterator)
mu_iterator_set_next (iterator, opitr_next);
mu_iterator_set_getitem (iterator, opitr_getitem);
mu_iterator_set_finished_p (iterator, opitr_finished_p);
mu_iterator_set_curitem_p (iterator, opitr_curitem_p);
mu_iterator_set_delitem (iterator, opitr_delitem);
mu_iterator_set_destroy (iterator, opitr_destroy);
mu_iterator_set_dup (iterator, opitr_data_dup);
......
......@@ -572,10 +572,11 @@ finished_p (void *owner)
}
static int
curitem_p (void *owner, void *item)
delitem (void *owner, void *item)
{
struct debug_iterator *itr = owner;
return mu_c_strcasecmp (cattab[itr->pos].name, (char *) item) == 0;
return mu_c_strcasecmp (cattab[itr->pos].name, (char *) item) == 0 ?
MU_ITR_DELITEM_NEXT : MU_ITR_DELITEM_NOTHING;
}
static int
......@@ -659,7 +660,7 @@ mu_debug_get_iterator (mu_iterator_t *piterator, int skipunset)
mu_iterator_set_next (iterator, next);
mu_iterator_set_getitem (iterator, getitem);
mu_iterator_set_finished_p (iterator, finished_p);
mu_iterator_set_curitem_p (iterator, curitem_p);
mu_iterator_set_delitem (iterator, delitem);
mu_iterator_set_dup (iterator, list_data_dup);
mu_iterator_set_itrctl (iterator, list_itrctl);
......
......@@ -84,10 +84,10 @@ destroy (mu_iterator_t iterator, void *data)
}
static int
curitem_p (void *owner, void *item)
delitem (void *owner, void *item)
{
struct list_iterator *itr = owner;
return itr->cur == item;
return itr->cur == item ? MU_ITR_DELITEM_NEXT : MU_ITR_DELITEM_NOTHING;
}
static int
......@@ -142,7 +142,7 @@ list_itrctl (void *owner, enum mu_itrctl_req req, void *arg)
ptr = itr->cur;
prev = ptr->prev;
mu_iterator_advance (list->itr, ptr);
mu_iterator_delitem (list->itr, ptr);
prev->next = ptr->next;
ptr->next->prev = prev;
if (req == mu_itrctl_delete)
......@@ -238,7 +238,7 @@ mu_list_get_iterator (mu_list_t list, mu_iterator_t *piterator)
mu_iterator_set_next (iterator, next);
mu_iterator_set_getitem (iterator, getitem);
mu_iterator_set_finished_p (iterator, finished_p);
mu_iterator_set_curitem_p (iterator, curitem_p);
mu_iterator_set_delitem (iterator, delitem);
mu_iterator_set_destroy (iterator, destroy);
mu_iterator_set_dup (iterator, list_data_dup);
mu_iterator_set_itrctl (iterator, list_itrctl);
......
......@@ -35,7 +35,7 @@ mu_list_pop (mu_list_t list, void **item)
last = list->head.prev;
prev = last->prev;
mu_iterator_advance (list->itr, last);
mu_iterator_delitem (list->itr, last);
prev->next = last->next;
prev->next->prev = prev;
if (item)
......
......@@ -41,7 +41,7 @@ mu_list_remove (mu_list_t list, void *item)
{
struct list_data *previous = current->prev;
mu_iterator_advance (list->itr, current);
mu_iterator_delitem (list->itr, current);
previous->next = current->next;
current->next->prev = previous;
DESTROY_ITEM (list, current);
......
......@@ -26,7 +26,6 @@ int
mu_list_remove_nth (mu_list_t list, size_t n)
{
struct list_data *current;
mu_list_comparator_t comp;
int status = MU_ERR_NOENT;
size_t i;
......@@ -42,7 +41,7 @@ mu_list_remove_nth (mu_list_t list, size_t n)
{
struct list_data *previous = current->prev;
mu_iterator_advance (list->itr, current);
mu_iterator_delitem (list->itr, current);
previous->next = current->next;
current->next->prev = previous;
DESTROY_ITEM (list, current);
......
......@@ -30,13 +30,20 @@ struct header_iterator
{
mu_header_t header;
size_t index;
int backwards;
};
static int
hdr_first (void *owner)
{
struct header_iterator *itr = owner;
itr->index = 1;
if (itr->backwards)
{
if (mu_header_get_field_count (itr->header, &itr->index))
return 1;
}
else
itr->index = 1;
return 0;
}
......@@ -44,7 +51,13 @@ static int
hdr_next (void *owner)
{
struct header_iterator *itr = owner;
itr->index++;
if (itr->backwards)
{
if (itr->index != 0)
itr->index--;
}
else
itr->index++;
return 0;
}
......@@ -58,16 +71,16 @@ hdr_getitem (void *owner, void **pret, const void **pkey)
rc = mu_header_get_field_count (itr->header, &count);
if (rc)
return rc;
if (itr->index > count)
if (itr->index < 1 || itr->index > count)
return MU_ERR_NOENT;
rc = mu_header_sget_field_name (itr->header, itr->index,
(const char**) pkey);
rc = mu_header_sget_field_value (itr->header, itr->index,
(const char**) pret);
if (rc == 0)
{
if (pkey)
rc = mu_header_sget_field_value (itr->header, itr->index,
(const char**) pret);
rc = mu_header_sget_field_name (itr->header, itr->index,
(const char**) pkey);
}
return rc;
}
......@@ -78,6 +91,9 @@ hdr_finished_p (void *owner)
struct header_iterator *itr = owner;
size_t count;
if (itr->backwards)
return itr->index < 1;
if (mu_header_get_field_count (itr->header, &count))
return 1;
return itr->index > count;
......@@ -93,13 +109,16 @@ hdr_destroy (mu_iterator_t iterator, void *data)
}
static int
hdr_curitem_p (void *owner, void *item)
hdr_delitem (void *owner, void *item)
{
void *ptr;
struct header_iterator *itr = owner;
const void *ptr;
if (hdr_getitem (owner, &ptr, NULL))
return 0;
return ptr == item;
if (mu_header_get_itemptr (itr->header, itr->index, &ptr))
return MU_ITR_DELITEM_NOTHING;
if (ptr == item && !itr->backwards)
return MU_ITR_DELITEM_ADVANCE;
return MU_ITR_DELITEM_NOTHING;
}
static int
......@@ -115,6 +134,51 @@ hdr_data_dup (void **ptr, void *owner)
return 0;
}
static int
hdr_itrctl (void *owner, enum mu_itrctl_req req, void *arg)
{
struct header_iterator *itr = owner;
switch (req)
{
case mu_itrctl_tell:
/* Return current position in the object */
if (hdr_finished_p (owner))
return MU_ERR_NOENT;
else
*(size_t*)arg = itr->index;
return 0;
break;
case mu_itrctl_delete:
/* Delete current element */
if (hdr_finished_p (owner))
return MU_ERR_NOENT;
else
return mu_header_remove (itr->header, NULL, itr->index);
break;
case mu_itrctl_qry_direction:
if (!arg)
return EINVAL;
else
*(int*)arg = itr->backwards;
break;
case mu_itrctl_set_direction:
if (!arg)
return EINVAL;
else
itr->backwards = !!*(int*)arg;
break;
default:
return ENOSYS;
}
return 0;
}
int
mu_header_get_iterator (mu_header_t hdr, mu_iterator_t *piterator)
{
......@@ -142,9 +206,10 @@ mu_header_get_iterator (mu_header_t hdr, mu_iterator_t *piterator)
mu_iterator_set_next (iterator, hdr_next);
mu_iterator_set_getitem (iterator, hdr_getitem);
mu_iterator_set_finished_p (iterator, hdr_finished_p);
mu_iterator_set_curitem_p (iterator, hdr_curitem_p);
mu_iterator_set_delitem (iterator, hdr_delitem);
mu_iterator_set_destroy (iterator, hdr_destroy);
mu_iterator_set_dup (iterator, hdr_data_dup);
mu_iterator_set_itrctl (iterator, hdr_itrctl);
mu_iterator_attach (&hdr->itr, iterator);
......
......@@ -80,13 +80,15 @@ mu_hdrent_find (struct _mu_header *hdr, const char *name, int pos)
if (pos > 0)
{
for (p = hdr->head; p; p = p->next)
if (mu_c_strcasecmp (MU_HDRENT_NAME (hdr,p), name) == 0 && pos-- == 1)
if ((!name || mu_c_strcasecmp (MU_HDRENT_NAME (hdr,p), name) == 0) &&
pos-- == 1)
break;
}
else if (pos < 0)
{
for (p = hdr->tail; p; p = p->prev)
if (mu_c_strcasecmp (MU_HDRENT_NAME (hdr,p), name) == 0 && ++pos == 0)
if ((!name || mu_c_strcasecmp (MU_HDRENT_NAME (hdr,p), name) == 0) &&
++pos == 0)
break;
}
else
......@@ -544,7 +546,7 @@ mu_header_remove (mu_header_t header, const char *fn, int n)
int status;
struct mu_hdrent *ent;
if (header == NULL || fn == NULL)
if (header == NULL)
return EINVAL;
status = mu_header_fill (header);
......@@ -555,6 +557,7 @@ mu_header_remove (mu_header_t header, const char *fn, int n)
if (!ent)
return MU_ERR_NOENT;
mu_iterator_delitem (header->itr, ent);
mu_hdrent_remove (header, ent);
HEADER_SET_MODIFIED (header);
free (ent);
......@@ -782,6 +785,26 @@ mu_header_get_field_count (mu_header_t header, size_t *pcount)
}
int
mu_header_get_itemptr (mu_header_t header, size_t num, const void **sptr)
{
int status;
if (header == NULL)
return EINVAL;
status = mu_header_fill (header);
if (status == 0)
{
struct mu_hdrent *ent = mu_hdrent_nth (header, num);
if (ent)
*sptr = ent;
else
status = MU_ERR_NOENT;
}
return status;
}
int
mu_header_sget_field_name (mu_header_t header, size_t num, const char **sptr)
{
int status;
......
......@@ -52,7 +52,7 @@ mu_message_get_imapenvelope (mu_message_t msg, struct mu_imapenvelope **pimapenv
if (msg == NULL)
return EINVAL;
if (imapenvelope == NULL)
if (pimapenvelope == NULL)
return MU_ERR_OUT_PTR_NULL;
if (msg->_imapenvelope)
return msg->_imapenvelope (msg, pimapenvelope);
......
......@@ -111,13 +111,14 @@ mbx_destroy (mu_iterator_t iterator, void *data)
}
static int
mbx_curitem_p (void *owner, void *item)
mbx_delitem (void *owner, void *item)
{
void *ptr;
if (mbx_getitem (owner, &ptr, NULL))
return 0;
return ptr == item;/* FIXME: Is it ok? */
return ptr == item ? MU_ITR_DELITEM_NEXT : MU_ITR_DELITEM_NOTHING;
/* FIXME: is it ok? */
}
static int
......@@ -210,7 +211,7 @@ mu_mailbox_get_iterator (mu_mailbox_t mbx, mu_iterator_t *piterator)
mu_iterator_set_next (iterator, mbx_next);
mu_iterator_set_getitem (iterator, mbx_getitem);
mu_iterator_set_finished_p (iterator, mbx_finished_p);
mu_iterator_set_curitem_p (iterator, mbx_curitem_p);
mu_iterator_set_delitem (iterator, mbx_delitem);
mu_iterator_set_destroy (iterator, mbx_destroy);
mu_iterator_set_dup (iterator, mbx_data_dup);
mu_iterator_set_itrctl (iterator, mbx_itrctl);
......
......@@ -16,6 +16,7 @@
moddir=@MU_SIEVE_MODDIR@
mod_LTLIBRARIES = \
editheader.la\
list.la\
moderator.la\
pipe.la\
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2012 Free Software Foundation, Inc.
GNU Mailutils 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, 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with GNU Mailutils. If not, see
<http://www.gnu.org/licenses/>. */
/* This module implements the Editheader Extension (RFC 5293) */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <mailutils/types.h>
#include <mailutils/message.h>
#include <mailutils/header.h>
#include <mailutils/error.h>
#include <mailutils/errno.h>
#include <mailutils/sieve.h>
/* Syntax: addheader [:last] <field-name: string> <value: string>
*/
int
sieve_addheader (mu_sieve_machine_t mach, mu_list_t args, mu_list_t tags)
{
mu_sieve_value_t *val;
const char *field_name;
const char *field_value;
mu_message_t msg;
mu_header_t hdr;
int rc;
val = mu_sieve_value_get (args, 0);
if (!val)
{
mu_sieve_error (mach, "%lu: %s",
(unsigned long) mu_sieve_get_message_num (mach),
_("cannot get field name"));
mu_sieve_abort (mach);
}
field_name = val->v.string;
val = mu_sieve_value_get (args, 1);
if (!val)
{
mu_sieve_error (mach, "%lu: %s",
(unsigned long) mu_sieve_get_message_num (mach),
_("cannot get field value"));
mu_sieve_abort (mach);
}
field_value = val->v.string;
mu_sieve_log_action (mach, "ADDHEADER", "%s: %s", field_name, field_value);
if (mu_sieve_is_dry_run (mach))
return 0;
msg = mu_sieve_get_message (mach);
rc = mu_message_get_header (msg, &hdr);
if (rc)
{
mu_sieve_error (mach, "%lu: %s: %s",
(unsigned long) mu_sieve_get_message_num (mach),
_("cannot get message header"),
mu_strerror (rc));
mu_sieve_abort (mach);
}
rc = (mu_sieve_tag_lookup (tags, "last", NULL) ?
mu_header_append : mu_header_prepend) (hdr, field_name, field_value);
if (rc)
{
mu_sieve_error (mach, "%lu: %s: %s",
(unsigned long) mu_sieve_get_message_num (mach),
_("cannot append message header"),
mu_strerror (rc));
mu_sieve_abort (mach);
}
return 0;
}
/* Syntax: deleteheader [:index <fieldno: number> [:last]]
[COMPARATOR] [MATCH-TYPE]
<field-name: string>
[<value-patterns: string-list>]
*/
int
sieve_deleteheader (mu_sieve_machine_t mach, mu_list_t args, mu_list_t tags)
{
mu_sieve_value_t *val;
const char *field_name;
const char *field_pattern;
mu_message_t msg;
mu_header_t hdr;
int rc;
mu_sieve_comparator_t comp;
mu_iterator_t itr;
unsigned long i, idx = 0;
val = mu_sieve_value_get (args, 0);
if (!val)
{
mu_sieve_error (mach, "%lu: %s",
(unsigned long) mu_sieve_get_message_num (mach),
_("cannot get field name"));
mu_sieve_abort (mach);
}
field_name = val->v.string;
val = mu_sieve_value_get (args, 1);
if (!val)
{
field_pattern = NULL;
mu_sieve_log_action (mach, "DELETEHEADER", "%s", field_name);
}
else
{
switch (val->type)
{
case SVT_STRING_LIST:
if (mu_list_get (val->v.list, 0, (void**)&field_pattern))
{
mu_sieve_error (mach, "%lu: %s",
(unsigned long) mu_sieve_get_message_num (mach),
_("cannot get list item"));
mu_sieve_abort (mach);
}
mu_sieve_log_action (mach, "DELETEHEADER", "%s: (regexp)",
field_name);
break;
case SVT_STRING:
field_pattern = val->v.string;
mu_sieve_log_action (mach, "DELETEHEADER", "%s: %s", field_name,
field_pattern);
break;
default:
mu_sieve_error (mach, "%lu: %s: %d",
(unsigned long) mu_sieve_get_message_num (mach),
_("unexpected value type"), val->type);
mu_sieve_abort (mach);
}
}
if (mu_sieve_is_dry_run (mach))
return 0;
msg = mu_sieve_get_message (mach);
rc = mu_message_get_header (msg, &hdr);
if (rc)
{
mu_sieve_error (mach, "%lu: %s: %s",
(unsigned long) mu_sieve_get_message_num (mach),
_("cannot get message header"),
mu_strerror (rc));
mu_sieve_abort (mach);
}
mu_header_get_iterator (hdr, &itr);
if (mu_sieve_tag_lookup (tags, "last", NULL))
{
int backwards = 1;
mu_iterator_ctl (itr, mu_itrctl_set_direction, &backwards);
}
comp = mu_sieve_get_comparator (mach, tags);
if (mu_sieve_tag_lookup (tags, "index", &val))
idx = val->v.number;
for (i = 0, mu_iterator_first (itr); !mu_iterator_is_done (itr);
mu_iterator_next (itr))
{
const char *fn, *fv;
mu_iterator_current_kv (itr, (const void **)&fn, (void **)&fv);
if (strcmp (field_name, fn))
continue;
if (idx && ++i < idx)
continue;
if (field_pattern)
{
if (comp (field_pattern, fv))
mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
}
else
mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
if (idx)
break;
}
mu_iterator_destroy (&itr);
return 0;
}
/* addheader tagged arguments: */
static mu_sieve_tag_def_t addheader_tags[] = {
{ "last", SVT_VOID },
{ NULL }
};
static mu_sieve_tag_group_t addheader_tag_groups[] = {
{ addheader_tags, NULL },
{ NULL }
};
/* addheader required arguments: */
static mu_sieve_data_type addheader_args[] = {
SVT_STRING, /* field name */
SVT_STRING, /* field value */
SVT_VOID
};
#if 0
/* FIXME: The checker interface should be redone. Until then this function
is commented out. Problems:
1. Checkers are called per group, there's no way to call them per tag.
2. See FIXMEs in the code.
*/
static int
index_checker (const char *name, mu_list_t tags, mu_list_t args)
{
mu_iterator_t itr;
mu_sieve_runtime_tag_t *match = NULL;
int err;
if (!tags || mu_list_get_iterator (tags, &itr))
return 0;
err = 0;
for (mu_iterator_first (itr); !err && !mu_iterator_is_done (itr);
mu_iterator_next (itr))
{
mu_sieve_runtime_tag_t *t;
mu_iterator_current (itr, (void **)&t);
if (strcmp (t->tag, "index") == 0)
{
if (match)
{
/* FIXME: 1. This function must be public.
2. locus should be included in t
*/
mu_sv_compile_error (&mu_sieve_locus,
_("index specified twice in call to `%s'"),
name);
err = 1;
break;
}
}
}
mu_iterator_destroy (&itr);
if (err)
return 1;
if (match)
{
if (match->arg->v.number < 1)
{
// See FIXME above
mu_sv_compile_error (&mu_sieve_locus,
_("invalid index value: %s"),
match->arg->v.string);
return 1;
}
}
return 0;
}
#endif
static mu_sieve_tag_def_t 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 }
};
/* deleteheader tagged arguments: */
static mu_sieve_tag_def_t deleteheader_tags[] = {
{ "last", SVT_VOID },
{ "index", SVT_NUMBER },
{ NULL }
};
static mu_sieve_tag_group_t deleteheader_tag_groups[] = {
{ deleteheader_tags, NULL },
{ match_part_tags, mu_sieve_match_part_checker },
{ NULL }
};
/* deleteheader required arguments: */
static mu_sieve_data_type deleteheader_args[] = {
SVT_STRING, /* field name or value pattern */
SVT_VOID
};
int
SIEVE_EXPORT (editheader, init) (mu_sieve_machine_t mach)
{
int rc;
/* This dummy record is required by libmu_sieve */
rc = mu_sieve_register_action (mach, "editheader", NULL, NULL, NULL, 1);
if (rc)
return rc;
rc = mu_sieve_register_action (mach, "addheader", sieve_addheader,
addheader_args, addheader_tag_groups, 1);
if (rc)
return rc;
rc = mu_sieve_register_action_ext (mach, "deleteheader", sieve_deleteheader,
deleteheader_args, deleteheader_args,
deleteheader_tag_groups,
1);
if (rc)
return rc;
return rc;
}
......@@ -139,6 +139,7 @@ mu_sv_code_command (mu_sieve_register_t *reg, mu_list_t arglist)
mu_list_t tag_list = NULL;
mu_list_t chk_list = NULL;
mu_sieve_data_type *exp_arg;
int opt_args = 0;
int rc, err = 0;
static mu_sieve_data_type empty[] = { SVT_VOID };
......@@ -237,16 +238,25 @@ mu_sv_code_command (mu_sieve_register_t *reg, mu_list_t arglist)
mu_list_append (chk_list, cf);
}
}
else if (*exp_arg == SVT_VOID)
{
mu_sv_compile_error (&mu_sieve_locus,
_("too many arguments in call to `%s'"),
reg->name);
err = 1;
break;
}
else
{
if (*exp_arg == SVT_VOID)
{
if (reg->opt_args)
{
exp_arg = reg->opt_args;
opt_args = 1;
}
else
{
mu_sv_compile_error (&mu_sieve_locus,
_("too many arguments in call to `%s'"),
reg->name);
err = 1;
break;
}
}
if (*exp_arg != val->type)
{
if (*exp_arg == SVT_STRING_LIST && val->type == SVT_STRING)
......@@ -291,7 +301,7 @@ mu_sv_code_command (mu_sieve_register_t *reg, mu_list_t arglist)
if (!err)
{
if (*exp_arg != SVT_VOID)
if (!opt_args && *exp_arg != SVT_VOID)
{
mu_sv_compile_error (&mu_sieve_locus,
_("too few arguments in call to `%s'"),
......
......@@ -50,13 +50,15 @@ reg_lookup (mu_list_t list, const char *name)
mu_sieve_register_t *
mu_sieve_test_lookup (mu_sieve_machine_t mach, const char *name)
{
return reg_lookup (mach->test_list, name);
mu_sieve_register_t *reg = reg_lookup (mach->test_list, name);
return (reg && reg->handler) ? reg : NULL;
}
mu_sieve_register_t *
mu_sieve_action_lookup (mu_sieve_machine_t mach, const char *name)
{
return reg_lookup (mach->action_list, name);
mu_sieve_register_t *reg = reg_lookup (mach->action_list, name);
return (reg && reg->handler) ? reg : NULL;
}
static int
......@@ -90,7 +92,8 @@ static int
sieve_register (mu_list_t *pool,
mu_list_t *list,
const char *name, mu_sieve_handler_t handler,
mu_sieve_data_type *arg_types,
mu_sieve_data_type *req_arg_types,
mu_sieve_data_type *opt_arg_types,
mu_sieve_tag_group_t *tags, int required)
{
mu_sieve_register_t *reg = mu_sieve_palloc (pool, sizeof (*reg));
......@@ -100,7 +103,8 @@ sieve_register (mu_list_t *pool,
reg->name = name;
reg->handler = handler;
reg->req_args = arg_types;
reg->req_args = req_arg_types;
reg->opt_args = opt_arg_types;
reg->tags = tags;
reg->required = required;
......@@ -119,23 +123,49 @@ sieve_register (mu_list_t *pool,
int
mu_sieve_register_test (mu_sieve_machine_t mach,
const char *name, mu_sieve_handler_t handler,
mu_sieve_data_type *arg_types,
mu_sieve_tag_group_t *tags, int required)
mu_sieve_register_test_ext (mu_sieve_machine_t mach,
const char *name, mu_sieve_handler_t handler,
mu_sieve_data_type *req_args,
mu_sieve_data_type *opt_args,
mu_sieve_tag_group_t *tags, int required)
{
return sieve_register (&mach->memory_pool,
&mach->test_list, name, handler,
arg_types, tags, required);
req_args, opt_args, tags, required);
}
int
mu_sieve_register_action (mu_sieve_machine_t mach,
const char *name, mu_sieve_handler_t handler,
mu_sieve_data_type *arg_types,
mu_sieve_tag_group_t *tags, int required)
mu_sieve_register_test (mu_sieve_machine_t mach,
const char *name, mu_sieve_handler_t handler,
mu_sieve_data_type *arg_types,
mu_sieve_tag_group_t *tags, int required)
{
return mu_sieve_register_test_ext (mach, name, handler,
arg_types, NULL,
tags,
required);
}
int
mu_sieve_register_action_ext (mu_sieve_machine_t mach,
const char *name, mu_sieve_handler_t handler,
mu_sieve_data_type *req_args,
mu_sieve_data_type *opt_args,
mu_sieve_tag_group_t *tags, int required)
{
return sieve_register (&mach->memory_pool,
&mach->action_list, name, handler,
arg_types, tags, required);
req_args, opt_args, tags, required);
}
int
mu_sieve_register_action (mu_sieve_machine_t mach,
const char *name, mu_sieve_handler_t handler,
mu_sieve_data_type *arg_types,
mu_sieve_tag_group_t *tags, int required)
{
return mu_sieve_register_action_ext (mach, name, handler,
arg_types, NULL,
tags,
required);
}
......
......@@ -426,8 +426,10 @@ mu_sieve_vlist_do (mu_sieve_value_t *val, mu_list_action_t ac, void *data)
case SVT_VALUE_LIST:
case SVT_STRING_LIST:
return mu_list_foreach (val->v.list, ac, data);
case SVT_STRING:
return ac (val->v.string, data);
default:
mu_error ("mu_sieve_vlist_do: unexpected list type %d", val->type);
return EINVAL;
}
}
......
......@@ -35,7 +35,7 @@ static int pop3_itr_destroy (mu_iterator_t itr, void *owner);
static int pop3_itr_first (void *owner);
static int pop3_itr_next (void *woner);
static int pop3_itr_getitem (void *owner, void **pret, const void **pkey);
static int pop3_itr_curitem_p (void *owner, void *data);
static int pop3_itr_delitem (void *owner, void *data);
static int pop3_itr_finished_p (void *owner);
struct pop3_iterator
......@@ -81,7 +81,7 @@ mu_pop3_iterator_create (mu_pop3_t pop3, mu_iterator_t *piterator)
mu_iterator_set_next (iterator, pop3_itr_next);
mu_iterator_set_getitem (iterator, pop3_itr_getitem);
mu_iterator_set_finished_p (iterator, pop3_itr_finished_p);
mu_iterator_set_curitem_p (iterator, pop3_itr_curitem_p);
mu_iterator_set_delitem (iterator, pop3_itr_delitem);
mu_iterator_set_destroy (iterator, pop3_itr_destroy);
mu_iterator_set_dup (iterator, pop3_itr_dup);
......@@ -183,9 +183,10 @@ pop3_itr_finished_p (void *owner)
}
static int
pop3_itr_curitem_p (void *owner, void *item)
pop3_itr_delitem (void *owner, void *item)
{
struct pop3_iterator *pop3_iterator = (struct pop3_iterator *)owner;
return *((char **)item) == pop3_iterator->item;
return *((char **)item) == pop3_iterator->item ?
MU_ITR_DELITEM_NEXT : MU_ITR_DELITEM_NOTHING;
}
......
......@@ -41,9 +41,11 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac
TESTSUITE_AT = \
action.at\
addheader.at\
address.at\
allof.at\
anyof.at\
delheader.at\
compile.at\
envelope.at\
exists.at\
......
# This file is part of GNU Mailutils. -*- Autotest -*-
# Copyright (C) 2012 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 <http://www.gnu.org/licenses/>.
AT_BANNER([addheader])
m4_pushdef([MUT_SIEVE_EXT_NAME],[addheader])
MUT_SIEVE_EXT_TEST([prepend header],[addheader addheader00],
[require "editheader";
addheader "X-Sieve-Filtered" "<kim@job.example.com>";
],
[MUT_MBCOPY($abs_top_srcdir/testsuite/spool/sieve.mbox,mailbox)
sieve MUT_SIEVE_CMDLINE MUT_SIEVE_OPTIONS -f ./mailbox prog
sed '/X-IMAPbase:/d;s/ *$//' mailbox
],
[From coyote@desert.example.org Sun May 6 22:16:47 2001
X-Sieve-Filtered: <kim@job.example.com>
From: coyote@desert.example.org
To: roadrunner@acme.example.com
Subject: I have a present for you
X-Caffeine: C8H10N4O2
Status:
X-UID: 1
Look, I'm sorry about the whole anvil thing, and I really
didn't mean to try and drop it on you from the top of the
cliff. I want to try to make it up to you. I've got some
great birdseed over here at my place--top of the line
stuff--and if you come by, I'll have it all wrapped up
for you. I'm really sorry for all the problems I've caused
for you over the years, but I know we can work this out.
--
Wile E. Coyote "Super Genius" coyote@desert.example.org
From b1ff@de.res.example.com Sun May 6 22:17:15 2001
X-Sieve-Filtered: <kim@job.example.com>
From: youcouldberich!@reply-by-postal-mail.invalid
To: rube@landru.example.edu
Subject: $$$ YOU, TOO, CAN BE A MILLIONAIRE! $$$
Date: TBD
X-Number: 0015
Status:
X-UID: 2
YOU MAY HAVE ALREADY WON TEN MILLION DOLLARS, BUT I DOUBT
IT! SO JUST POST THIS TO SIX HUNDRED NEWSGROUPS! IT WILL
GUARANTEE THAT YOU GET AT LEAST FIVE RESPONSES WITH MONEY!
MONEY! MONEY! COLD HARD CASH! YOU WILL RECEIVE OVER
$20,000 IN LESS THAN TWO MONTHS! AND IT'S LEGAL!!!!!!!!!
!!!!!!!!!!!!!!!!!!111111111!!!!!!!11111111111!!1 JUST
SEND $5 IN SMALL, UNMARKED BILLS TO THE ADDRESSES BELOW!
From bar@dontmailme.org Fri Dec 28 23:28:09 2001
X-Sieve-Filtered: <kim@job.example.com>
Received: (from bar@dontmailme.org)
by dontmailme.org id fERKR9N16790
for foobar@nonexistent.net; Fri, 28 Dec 2001 22:18:08 +0200
Date: Fri, 28 Dec 2001 23:28:08 +0200
From: Bar <bar@dontmailme.org>
To: Foo Bar <foobar@nonexistent.net>
Message-Id: <200112232808.fERKR9N16790@dontmailme.org>
Subject: Coffee
Status:
X-UID: 3
How about some coffee?
],
[ADDHEADER on msg uid 1: X-Sieve-Filtered: <kim@job.example.com>
ADDHEADER on msg uid 2: X-Sieve-Filtered: <kim@job.example.com>
ADDHEADER on msg uid 3: X-Sieve-Filtered: <kim@job.example.com>
])
MUT_SIEVE_EXT_TEST([append header],[addheader addheader01],
[require "editheader";
addheader :last "X-Sieve-Filtered" "<kim@job.example.com>";
],
[MUT_MBCOPY($abs_top_srcdir/testsuite/spool/sieve.mbox,mailbox)
sieve MUT_SIEVE_CMDLINE MUT_SIEVE_OPTIONS -f ./mailbox prog
sed '/X-IMAPbase:/d;s/ *$//' mailbox
],
[From coyote@desert.example.org Sun May 6 22:16:47 2001
From: coyote@desert.example.org
To: roadrunner@acme.example.com
Subject: I have a present for you
X-Caffeine: C8H10N4O2
X-Sieve-Filtered: <kim@job.example.com>
Status:
X-UID: 1
Look, I'm sorry about the whole anvil thing, and I really
didn't mean to try and drop it on you from the top of the
cliff. I want to try to make it up to you. I've got some
great birdseed over here at my place--top of the line
stuff--and if you come by, I'll have it all wrapped up
for you. I'm really sorry for all the problems I've caused
for you over the years, but I know we can work this out.
--
Wile E. Coyote "Super Genius" coyote@desert.example.org
From b1ff@de.res.example.com Sun May 6 22:17:15 2001
From: youcouldberich!@reply-by-postal-mail.invalid
To: rube@landru.example.edu
Subject: $$$ YOU, TOO, CAN BE A MILLIONAIRE! $$$
Date: TBD
X-Number: 0015
X-Sieve-Filtered: <kim@job.example.com>
Status:
X-UID: 2
YOU MAY HAVE ALREADY WON TEN MILLION DOLLARS, BUT I DOUBT
IT! SO JUST POST THIS TO SIX HUNDRED NEWSGROUPS! IT WILL
GUARANTEE THAT YOU GET AT LEAST FIVE RESPONSES WITH MONEY!
MONEY! MONEY! COLD HARD CASH! YOU WILL RECEIVE OVER
$20,000 IN LESS THAN TWO MONTHS! AND IT'S LEGAL!!!!!!!!!
!!!!!!!!!!!!!!!!!!111111111!!!!!!!11111111111!!1 JUST
SEND $5 IN SMALL, UNMARKED BILLS TO THE ADDRESSES BELOW!
From bar@dontmailme.org Fri Dec 28 23:28:09 2001
Received: (from bar@dontmailme.org)
by dontmailme.org id fERKR9N16790
for foobar@nonexistent.net; Fri, 28 Dec 2001 22:18:08 +0200
Date: Fri, 28 Dec 2001 23:28:08 +0200
From: Bar <bar@dontmailme.org>
To: Foo Bar <foobar@nonexistent.net>
Message-Id: <200112232808.fERKR9N16790@dontmailme.org>
Subject: Coffee
X-Sieve-Filtered: <kim@job.example.com>
Status:
X-UID: 3
How about some coffee?
],
[ADDHEADER on msg uid 1: X-Sieve-Filtered: <kim@job.example.com>
ADDHEADER on msg uid 2: X-Sieve-Filtered: <kim@job.example.com>
ADDHEADER on msg uid 3: X-Sieve-Filtered: <kim@job.example.com>
])
m4_popdef([MUT_SIEVE_EXT_NAME])
\ No newline at end of file
# This file is part of GNU Mailutils. -*- Autotest -*-
# Copyright (C) 2012 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 <http://www.gnu.org/licenses/>.
AT_BANNER([deleteheader])
m4_pushdef([MUT_SIEVE_EXT_NAME],[deleteheader])
m4_define([mkmailbox],[
AT_DATA([$1],
[From coyote@desert.example.org Sun May 6 22:16:47 2001
From: coyote@desert.example.org
To: roadrunner@acme.example.com
X-Agent: Agent A
Received: (from bar@example.org)
by example.org id fERKR9N16790
for roadrunner@example.com; Fri, 28 Dec 2001 22:18:08 +0200
X-Agent: Agent 22
X-Agent: Agent C
Subject: Ping
Test message, please discard.
])])
MUT_SIEVE_EXT_TEST([delete all],[deleteheader delheader delheader00],
[require "editheader";
deleteheader "X-Agent";
],
[mkmailbox(mailbox)
sieve MUT_SIEVE_CMDLINE MUT_SIEVE_OPTIONS -f ./mailbox prog
sed '/^X-IMAPbase:/d;/^Status:/d;/^X-UID:/d;s/ *$//' mailbox
],
[From coyote@desert.example.org Sun May 6 22:16:47 2001
From: coyote@desert.example.org
To: roadrunner@acme.example.com
Received: (from bar@example.org)
by example.org id fERKR9N16790
for roadrunner@example.com; Fri, 28 Dec 2001 22:18:08 +0200
Subject: Ping
Test message, please discard.
],
[DELETEHEADER on msg uid 1: X-Agent
])
MUT_SIEVE_EXT_TEST([delete index],[deleteheader delheader delheader01],
[require "editheader";
deleteheader :index 1 "X-Agent";
],
[mkmailbox(mailbox)
sieve MUT_SIEVE_CMDLINE MUT_SIEVE_OPTIONS -f ./mailbox prog
sed '/^X-IMAPbase:/d;/^Status:/d;/^X-UID:/d;s/ *$//' mailbox
],
[From coyote@desert.example.org Sun May 6 22:16:47 2001
From: coyote@desert.example.org
To: roadrunner@acme.example.com
Received: (from bar@example.org)
by example.org id fERKR9N16790
for roadrunner@example.com; Fri, 28 Dec 2001 22:18:08 +0200
X-Agent: Agent 22
X-Agent: Agent C
Subject: Ping
Test message, please discard.
],
[DELETEHEADER on msg uid 1: X-Agent
])
MUT_SIEVE_EXT_TEST([delete index backwards],[deleteheader delheader delheader02],
[require "editheader";
deleteheader :index 2 :last "X-Agent";
],
[mkmailbox(mailbox)
sieve MUT_SIEVE_CMDLINE MUT_SIEVE_OPTIONS -f ./mailbox prog
sed '/^X-IMAPbase:/d;/^Status:/d;/^X-UID:/d;s/ *$//' mailbox
],
[From coyote@desert.example.org Sun May 6 22:16:47 2001
From: coyote@desert.example.org
To: roadrunner@acme.example.com
X-Agent: Agent A
Received: (from bar@example.org)
by example.org id fERKR9N16790
for roadrunner@example.com; Fri, 28 Dec 2001 22:18:08 +0200
X-Agent: Agent C
Subject: Ping
Test message, please discard.
],
[DELETEHEADER on msg uid 1: X-Agent
])
MUT_SIEVE_EXT_TEST([delete regex],[deleteheader delheader delheader03],
[require "editheader";
deleteheader :regex "X-Agent" "Agent [[A-Z]]";
],
[mkmailbox(mailbox)
sieve MUT_SIEVE_CMDLINE MUT_SIEVE_OPTIONS -f ./mailbox prog
sed '/^X-IMAPbase:/d;/^Status:/d;/^X-UID:/d;s/ *$//' mailbox
],
[From coyote@desert.example.org Sun May 6 22:16:47 2001
From: coyote@desert.example.org
To: roadrunner@acme.example.com
Received: (from bar@example.org)
by example.org id fERKR9N16790
for roadrunner@example.com; Fri, 28 Dec 2001 22:18:08 +0200
X-Agent: Agent 22
Subject: Ping
Test message, please discard.
],
[DELETEHEADER on msg uid 1: X-Agent: (regexp)
])
m4_popdef([MUT_SIEVE_EXT_NAME])
......@@ -147,6 +147,7 @@ m4_include([moderator.at])
m4_include([pipeact.at])
m4_include([pipetest.at])
m4_include([list.at])
m4_include([addheader.at])
m4_include([delheader.at])
......