Commit 37fc6c3c 37fc6c3c41b16823e9b130a2a425ed40eebf09f8 by Sergey Poznyakoff

Use mu_msgset_t in imap4d.

* imap4d/util.c (util_parse_msgset): Remove. Use mu_msgset_parse_imap.
All uses changed.
(util_foreach_message): Change first argument to mu_msgset_t.
All uses changed.
* imap4d/imap4d.h: Likewise.

* imap4d/copy.c: Use mu_msgset_t
* imap4d/fetch.c: Likewise.
* imap4d/search.c: Likewise.
* imap4d/store.c: Likewise.
1 parent 0ff01881
......@@ -124,7 +124,7 @@ do_copy (size_t msgno, void *data)
}
static int
try_copy (mu_mailbox_t dst, mu_mailbox_t src, mu_list_t msglist,
try_copy (mu_mailbox_t dst, mu_mailbox_t src, mu_msgset_t msgset,
char **err_text)
{
int rc;
......@@ -139,7 +139,7 @@ try_copy (mu_mailbox_t dst, mu_mailbox_t src, mu_list_t msglist,
*env.err_text = "Operation failed";
/* Check size */
rc = util_foreach_message (msglist, size_sum, &env);
rc = util_foreach_message (msgset, size_sum, &env);
if (rc)
return RESP_NO;
if (env.ret != RESP_OK)
......@@ -151,7 +151,7 @@ try_copy (mu_mailbox_t dst, mu_mailbox_t src, mu_list_t msglist,
return RESP_NO;
}
env.total = 0;
rc = util_foreach_message (msglist, do_copy, &env);
rc = util_foreach_message (msgset, do_copy, &env);
quota_update (env.total);
if (rc)
return RESP_NO;
......@@ -159,7 +159,7 @@ try_copy (mu_mailbox_t dst, mu_mailbox_t src, mu_list_t msglist,
}
static int
safe_copy (mu_mailbox_t dst, mu_mailbox_t src, mu_list_t msglist,
safe_copy (mu_mailbox_t dst, mu_mailbox_t src, mu_msgset_t msgset,
char **err_text)
{
size_t nmesg;
......@@ -174,7 +174,7 @@ safe_copy (mu_mailbox_t dst, mu_mailbox_t src, mu_list_t msglist,
return RESP_NO;
}
status = try_copy (dst, src, msglist, err_text);
status = try_copy (dst, src, msgset, err_text);
if (status != RESP_OK)
{
size_t maxmesg;
......@@ -228,8 +228,8 @@ int
imap4d_copy0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
{
int status;
char *msgset;
mu_list_t msglist;
char *msgset_str;
mu_msgset_t msgset;
char *name;
char *mailbox_name;
char *end;
......@@ -244,12 +244,19 @@ imap4d_copy0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
return 1;
}
msgset = imap4d_tokbuf_getarg (tok, arg);
msgset_str = imap4d_tokbuf_getarg (tok, arg);
name = imap4d_tokbuf_getarg (tok, arg + 1);
/* Get the message numbers in set[]. */
status = util_parse_msgset (msgset, isuid, mbox, &msglist, &end);
status = mu_msgset_create (&msgset, mbox, isuid ? MU_MSGSET_UID : 0);
if (!status)
{
*err_text = "Software error";
return RESP_BAD;
}
status = mu_msgset_parse_imap (msgset, msgset_str, &end);
if (status)
{
mu_msgset_free (msgset);
*err_text = "Error parsing message set";
/* FIXME: print error location */
return RESP_BAD;
......@@ -259,6 +266,7 @@ imap4d_copy0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
if (!mailbox_name)
{
mu_msgset_free (msgset);
*err_text = "Copy failed";
return RESP_NO;
}
......@@ -272,13 +280,15 @@ imap4d_copy0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
status = mu_mailbox_open (cmbox, MU_STREAM_RDWR | mailbox_mode[ns]);
if (status == 0)
{
mu_list_t msglist;
mu_msgset_get_list (msgset, &msglist);
if (!mu_list_is_empty (msglist))
status = safe_copy (cmbox, mbox, msglist, err_text);
status = safe_copy (cmbox, mbox, msgset, err_text);
mu_mailbox_close (cmbox);
}
mu_mailbox_destroy (&cmbox);
}
mu_list_destroy (&msglist);
mu_msgset_free (msgset);
if (status == 0)
{
......
......@@ -64,7 +64,7 @@ struct fetch_parse_closure
{
int isuid;
mu_list_t fnlist;
mu_list_t msgnumlist;
mu_msgset_t msgset;
};
......@@ -1753,18 +1753,22 @@ static int
fetch_thunk (imap4d_parsebuf_t pb)
{
int status;
char *msgset;
char *mstr;
char *end;
struct fetch_parse_closure *pclos = imap4d_parsebuf_data (pb);
msgset = imap4d_parsebuf_next (pb, 1);
mstr = imap4d_parsebuf_next (pb, 1);
status = mu_msgset_create (&pclos->msgset, mbox,
pclos->isuid ? MU_MSGSET_UID : 0);
if (status)
imap4d_parsebuf_exit (pb, "Software error");
/* Parse sequence numbers. */
status = util_parse_msgset (msgset, pclos->isuid, mbox,
&pclos->msgnumlist, &end);
status = mu_msgset_parse_imap (pclos->msgset, mstr, &end);
if (status)
imap4d_parsebuf_exit (pb, "Failed to parse message set");
/* Compile the expression */
/* Server implementations MUST implicitly
......@@ -1829,12 +1833,12 @@ imap4d_fetch0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
loop below */
frc.err_text = "Completed";
util_foreach_message (pclos.msgnumlist, _fetch_from_message, &frc);
util_foreach_message (pclos.msgset, _fetch_from_message, &frc);
mu_list_destroy (&frc.msglist);
}
mu_list_destroy (&pclos.fnlist);
mu_list_destroy (&pclos.msgnumlist);
mu_msgset_free (pclos.msgset);
return rc;
}
......
......@@ -107,6 +107,7 @@
#include <mailutils/prog.h>
#include <mailutils/imapio.h>
#include <mailutils/imaputil.h>
#include <mailutils/msgset.h>
#include <mu_umaxtostr.h>
#include <muaux.h>
......@@ -371,12 +372,9 @@ extern char *util_getfullpath (const char *);
extern struct imap4d_command *util_getcommand (char *,
struct imap4d_command []);
extern int util_parse_msgset (char *s, int isuid, mu_mailbox_t mbx,
mu_list_t *plist, char **end);
typedef int (*imap4d_message_action_t) (size_t, void *);
int util_foreach_message (mu_list_t list, imap4d_message_action_t action,
int util_foreach_message (mu_msgset_t list, imap4d_message_action_t action,
void *data);
enum datetime_parse_mode /* how to parse date/time strings */
......
......@@ -62,7 +62,7 @@ struct value
char *string;
mu_off_t number;
time_t date;
mu_list_t msgset;
mu_msgset_t msgset;
} v;
};
......@@ -198,11 +198,12 @@ struct cond_equiv equiv_list[] =
};
/* A memory allocation chain used to keep track of objects allocated during
the recursive-descent parsing. */
the recursive-descend parsing. */
struct mem_chain
{
struct mem_chain *next;
void *mem;
void (*free_fun) (void *);
};
/* Code and stack sizes for execution of compiled search statement */
......@@ -233,7 +234,7 @@ struct parsebuf
};
static void parse_free_mem (struct parsebuf *pb);
static void *parse_regmem (struct parsebuf *pb, void *mem);
static void *parse_regmem (struct parsebuf *pb, void *mem, void (*f)(void*));
static char *parse_strdup (struct parsebuf *pb, char *s);
static void *parse_alloc (struct parsebuf *pb, size_t size);
static struct search_node *parse_search_key_list (struct parsebuf *pb);
......@@ -385,7 +386,10 @@ parse_free_mem (struct parsebuf *pb)
while (alloc)
{
next = alloc->next;
free (alloc->mem);
if (alloc->free_fun)
alloc->free_fun (alloc->mem);
else
free (alloc->mem);
free (alloc);
alloc = next;
}
......@@ -393,7 +397,7 @@ parse_free_mem (struct parsebuf *pb)
/* Register a memory pointer mem with the parsebuf */
void *
parse_regmem (struct parsebuf *pb, void *mem)
parse_regmem (struct parsebuf *pb, void *mem, void (*f) (void*))
{
struct mem_chain *mp;
......@@ -403,6 +407,7 @@ parse_regmem (struct parsebuf *pb, void *mem)
mp->next = pb->alloc;
pb->alloc = mp;
mp->mem = mem;
mp->free_fun = f;
return mem;
}
......@@ -413,7 +418,7 @@ parse_alloc (struct parsebuf *pb, size_t size)
void *p = malloc (size);
if (!p)
imap4d_bye (ERR_NO_MEM);
return parse_regmem (pb, p);
return parse_regmem (pb, p, NULL);
}
/* Create a copy of the string. */
......@@ -423,18 +428,34 @@ parse_strdup (struct parsebuf *pb, char *s)
s = strdup (s);
if (!s)
imap4d_bye (ERR_NO_MEM);
return parse_regmem (pb, s);
return parse_regmem (pb, s, NULL);
}
static void
free_msgset (void *ptr)
{
mu_msgset_free (ptr);
}
mu_msgset_t
parse_msgset_create (struct parsebuf *pb, mu_mailbox_t mbox, int flags)
{
mu_msgset_t msgset;
if (mu_msgset_create (&msgset, mbox, flags))
imap4d_bye (ERR_NO_MEM);
return parse_regmem (pb, msgset, free_msgset);
}
/* A recursive-descent parser for the following grammar:
search_key_list : search_key
| search_key_list search_key
;
| search_key_list search_key
;
search_key : simple_key
| NOT simple_key
| OR simple_key simple_key
| '(' search_key_list ')'
| NOT simple_key
| OR simple_key simple_key
| '(' search_key_list ')'
;
*/
......@@ -445,7 +466,7 @@ struct search_node *
parse_search_key_list (struct parsebuf *pb)
{
struct search_node *leftarg = NULL;
while (pb->token && pb->token[0] != ')')
{
struct search_node *rightarg = parse_search_key (pb);
......@@ -478,7 +499,7 @@ parse_search_key (struct parsebuf *pb)
node = parse_search_key_list (pb);
if (!node)
return NULL;
if (strcmp (pb->token, ")"))
{
pb->err_mesg = "Unbalanced parenthesis";
......@@ -500,30 +521,30 @@ parse_search_key (struct parsebuf *pb)
else if (mu_c_strcasecmp (pb->token, "NOT") == 0)
{
struct search_node *np;
if (parse_gettoken (pb, 1) == 0)
return NULL;
return NULL;
np = parse_search_key (pb);
if (!np)
return NULL;
return NULL;
node = parse_alloc (pb, sizeof *node);
node->type = node_not;
node->v.arg[0] = np;
return node;
}
else if (mu_c_strcasecmp (pb->token, "OR") == 0)
{
struct search_node *leftarg, *rightarg;
if (parse_gettoken (pb, 1) == 0)
return NULL;
return NULL;
if ((leftarg = parse_search_key (pb)) == NULL
|| (rightarg = parse_search_key (pb)) == NULL)
return NULL;
|| (rightarg = parse_search_key (pb)) == NULL)
return NULL;
node = parse_alloc (pb, sizeof *node);
node->type = node_or;
......@@ -543,9 +564,9 @@ parse_equiv_key (struct parsebuf *pb)
struct cond_equiv *condp;
int save_arg;
imap4d_tokbuf_t save_tok;
for (condp = equiv_list; condp->name && mu_c_strcasecmp (condp->name, pb->token);
condp++)
condp++)
;
if (!condp->name)
......@@ -563,11 +584,11 @@ parse_equiv_key (struct parsebuf *pb)
{
/* shouldn't happen? */
mu_diag_output (MU_DIAG_CRIT, _("%s:%d: INTERNAL ERROR (please report)"),
__FILE__, __LINE__);
__FILE__, __LINE__);
abort ();
}
imap4d_tokbuf_destroy (&pb->tok);
pb->arg = save_arg;
pb->tok = save_tok;
parse_gettoken (pb, 0);
......@@ -580,21 +601,22 @@ parse_simple_key (struct parsebuf *pb)
struct search_node *node;
struct cond *condp;
time_t time;
for (condp = condlist; condp->name && mu_c_strcasecmp (condp->name, pb->token);
condp++)
;
if (!condp->name)
{
mu_list_t msglist;
mu_msgset_t msgset = parse_msgset_create (pb, mbox,
pb->isuid ? MU_MSGSET_UID : 0);
if (util_parse_msgset (pb->token, pb->isuid, mbox, &msglist, NULL) == 0)
if (mu_msgset_parse_imap (msgset, pb->token, NULL) == 0)
{
struct search_node *np = parse_alloc (pb, sizeof *np);
np->type = node_value;
np->v.value.type = value_msgset;
np->v.value.v.msgset = msglist;
np->v.value.v.msgset = msgset;
node = parse_alloc (pb, sizeof *node);
node->type = node_call;
......@@ -602,7 +624,7 @@ parse_simple_key (struct parsebuf *pb)
node->v.key.narg = 1;
node->v.key.arg[0] = np;
node->v.key.fun = cond_msgset;
parse_gettoken (pb, 0);
return node;
......@@ -613,7 +635,7 @@ parse_simple_key (struct parsebuf *pb)
return NULL;
}
}
node = parse_alloc (pb, sizeof *node);
node->type = node_call;
node->v.key.keyword = condp->name;
......@@ -674,9 +696,11 @@ parse_simple_key (struct parsebuf *pb)
break;
case 'u': /* UID message set */
if (util_parse_msgset (pb->token, 0, NULL,
&arg->v.value.v.msgset, NULL))
arg->v.value.v.msgset = parse_msgset_create (pb, NULL, 0);
if (mu_msgset_parse_imap (arg->v.value.v.msgset, pb->token,
NULL))
{
mu_msgset_free (arg->v.value.v.msgset);
pb->err_mesg = "Bogus number set";
return NULL;
}
......@@ -853,7 +877,7 @@ static void
cond_msgset (struct parsebuf *pb, struct search_node *node, struct value *arg,
struct value *retval)
{
int rc = mu_list_locate (arg[0].v.msgset, &pb->msgno, NULL);
int rc = mu_msgset_locate (arg[0].v.msgset, pb->msgno, NULL);
retval->type = value_number;
retval->v.number = rc == 0;
}
......@@ -1075,7 +1099,7 @@ cond_uid (struct parsebuf *pb, struct search_node *node, struct value *arg,
size_t uid = 0;
mu_message_get_uid (pb->msg, &uid);
rc = mu_list_locate (arg[0].v.msgset, &pb->msgno, NULL);
rc = mu_msgset_locate (arg[0].v.msgset, pb->msgno, NULL);
retval->type = value_number;
retval->v.number = rc == 0;
}
......
......@@ -25,19 +25,19 @@ struct store_parse_closure
int ack;
int type;
int isuid;
mu_list_t msgnumlist;
mu_msgset_t msgset;
};
static int
store_thunk (imap4d_parsebuf_t p)
{
struct store_parse_closure *pclos = imap4d_parsebuf_data (p);
char *msgset;
char *mstr;
char *data;
int status;
char *end;
msgset = imap4d_parsebuf_next (p, 1);
mstr = imap4d_parsebuf_next (p, 1);
data = imap4d_parsebuf_next (p, 1);
if (*data == '+')
......@@ -69,9 +69,13 @@ store_thunk (imap4d_parsebuf_t p)
imap4d_parsebuf_exit (p, "Bogus data suffix");
}
status = mu_msgset_create (&pclos->msgset, mbox,
pclos->isuid ? MU_MSGSET_UID : 0);
if (status)
imap4d_parsebuf_exit (p, "Software error");
/* Get the message numbers in set[]. */
status = util_parse_msgset (msgset, pclos->isuid, mbox,
&pclos->msgnumlist, &end);
status = mu_msgset_parse_imap (pclos->msgset, mstr, &end);
if (status)
imap4d_parsebuf_exit (p, "Failed to parse message set");
......@@ -150,12 +154,12 @@ imap4d_store0 (imap4d_tokbuf_t tok, int isuid, char **ptext)
ptext);
if (rc == RESP_OK)
{
util_foreach_message (pclos.msgnumlist, _do_store, &pclos);
util_foreach_message (pclos.msgset, _do_store, &pclos);
*ptext = "Completed";
}
mu_list_destroy (&pclos.msgnumlist);
mu_msgset_free (pclos.msgset);
return rc;
}
......
......@@ -39,279 +39,6 @@ util_getfullpath (const char *name)
return mu_normalize_path (exp);
}
/* A message set is defined as:
set ::= sequence_num / (sequence_num ":" sequence_num) / (set "," set)
sequence_num ::= nz_number / "*"
;; * is the largest number in use. For message
;; sequence numbers, it is the number of messages
;; in the mailbox. For unique identifiers, it is
;; the unique identifier of the last message in
;; the mailbox.
nz_number ::= digit_nz *digit
Non-existing sequence numbers are ignored, except when they form part
of a message range (sequence_num ":" sequence_num), in which case they
are treated as minimal or maximal sequence numbers (uids) available in
the mailbox depending on whether they appear to the left or to the right
of ":".
*/
/* Message set is a list of non-overlapping msgranges, in ascending
order. No matter what command flavor is used, msg_beg and msg_end are
treated as sequence numbers (not UIDs). */
struct msgrange
{
size_t msg_beg; /* beginning message number */
size_t msg_end; /* ending message number */
};
/* Comparator function used to sort the message set list. */
static int
compare_msgrange (const void *a, const void *b)
{
struct msgrange const *sa = a;
struct msgrange const *sb = b;
if (sa->msg_beg < sb->msg_beg)
return -1;
if (sa->msg_beg > sb->msg_beg)
return 1;
if (sa->msg_end < sb->msg_end)
return -1;
if (sa->msg_end > sb->msg_end)
return 1;
return 0;
}
/* Comparator function used to locate a message in the list. Second argument
(b) is a pointer to the message number. */
static int
compare_msgnum (const void *a, const void *b)
{
struct msgrange const *range = a;
size_t msgno = *(size_t*)b;
if (range->msg_beg <= msgno && msgno <= range->msg_end)
return 0;
return 1;
}
/* This structure keeps parser state while parsing message set. */
struct parse_msgnum_env
{
char *s; /* Current position in string */
size_t minval; /* Min. sequence number or UID */
size_t maxval; /* Max. sequence number or UID */
mu_list_t list; /* List being built. */
int isuid:1; /* True, if parsing an UID command. */
mu_mailbox_t mailbox; /* Reference mailbox (can be NULL). */
};
/* Get a single message number/UID from env->s and store it into *PN.
Return 0 on success and error code on error.
Advance env->s to the point past the parsed message number.
*/
static int
get_msgnum (struct parse_msgnum_env *env, size_t *pn)
{
size_t msgnum;
char *p;
errno = 0;
msgnum = strtoul (env->s, &p, 10);
if (msgnum == ULONG_MAX && errno == ERANGE)
return MU_ERR_PARSE;
env->s = p;
if (msgnum > env->maxval)
msgnum = env->maxval;
*pn = msgnum;
return 0;
}
/* Parse a single message range (A:B). Treat '*' as the largest number/UID
in use. */
static int
parse_msgrange (struct parse_msgnum_env *env)
{
int rc;
struct msgrange msgrange, *mp;
if (*env->s == '*')
{
msgrange.msg_beg = env->maxval;
env->s++;
}
else if ((rc = get_msgnum (env, &msgrange.msg_beg)))
return rc;
if (*env->s == ':')
{
if (*++env->s == '*')
{
msgrange.msg_end = env->maxval;
++env->s;
}
else if (*env->s == 0)
return MU_ERR_PARSE;
else if ((rc = get_msgnum (env, &msgrange.msg_end)))
return rc;
}
else
msgrange.msg_end = msgrange.msg_beg;
if (msgrange.msg_end < msgrange.msg_beg)
{
size_t tmp = msgrange.msg_end;
msgrange.msg_end = msgrange.msg_beg;
msgrange.msg_beg = tmp;
}
if (env->isuid && env->mailbox)
{
int rc;
rc = mu_mailbox_translate (env->mailbox,
MU_MAILBOX_UID_TO_MSGNO,
msgrange.msg_beg, &msgrange.msg_beg);
if (rc == MU_ERR_NOENT)
msgrange.msg_beg = env->minval;
else if (rc)
return rc;
rc = mu_mailbox_translate (env->mailbox,
MU_MAILBOX_UID_TO_MSGNO,
msgrange.msg_end, &msgrange.msg_end);
if (rc == MU_ERR_NOENT)
msgrange.msg_end = env->maxval;
else if (rc)
return rc;
}
mp = malloc (sizeof (*mp));
if (!mp)
return ENOMEM;
*mp = msgrange;
rc = mu_list_append (env->list, mp);
if (rc)
free (mp);
return rc;
}
/* Parse message set specification S. ISUID indicates if the set is supposed
to contain UIDs (they will be translated to message sequence numbers).
MBX is a reference mailbox or NULL.
On success, return 0 and place the resulting set into *PLIST. On error,
return error code and point END to the position in the input string where
the parsing failed. */
int
util_parse_msgset (char *s, int isuid, mu_mailbox_t mbx,
mu_list_t *plist, char **end)
{
int rc;
struct parse_msgnum_env env;
size_t n;
if (!s)
return EINVAL;
if (!*s)
return MU_ERR_PARSE;
memset (&env, 0, sizeof (env));
env.s = s;
if (mbox)
{
size_t lastmsgno; /* Max. sequence number. */
rc = mu_mailbox_messages_count (mbx, &lastmsgno);
if (rc == 0)
{
if (isuid)
{
rc = mu_mailbox_translate (mbox, MU_MAILBOX_MSGNO_TO_UID,
lastmsgno, &env.maxval);
if (rc == 0)
rc = mu_mailbox_translate (mbox, MU_MAILBOX_MSGNO_TO_UID,
1, &env.minval);
}
else
env.maxval = lastmsgno;
}
if (rc)
return rc;
}
rc = mu_list_create (&env.list);
if (rc)
return rc;
mu_list_set_destroy_item (env.list, mu_list_free_item);
env.isuid = isuid;
env.mailbox = mbx;
while ((rc = parse_msgrange (&env)) == 0 && *env.s)
{
if (*env.s != ',' || *++env.s == 0)
{
rc = MU_ERR_PARSE;
break;
}
}
if (end)
*end = env.s;
if (rc)
{
mu_list_destroy (&env.list);
return rc;
}
mu_list_count (env.list, &n);
if (n > 1)
{
mu_iterator_t itr;
struct msgrange *last = NULL;
/* Sort the list and coalesce overlapping message ranges. */
mu_list_sort (env.list, compare_msgrange);
mu_list_get_iterator (env.list, &itr);
for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
mu_iterator_next (itr))
{
struct msgrange *msgrange;
mu_iterator_current (itr, (void**)&msgrange);
if (last)
{
if (last->msg_beg <= msgrange->msg_beg &&
msgrange->msg_beg <= last->msg_end)
{
if (msgrange->msg_end > last->msg_end)
last->msg_end = msgrange->msg_end;
mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
continue;
}
}
last = msgrange;
}
mu_iterator_destroy (&itr);
}
if (rc == 0)
{
mu_list_set_comparator (env.list, compare_msgnum);
*plist = env.list;
}
else
mu_list_destroy (&env.list);
return rc;
}
struct action_closure
{
imap4d_message_action_t action;
......@@ -321,7 +48,7 @@ struct action_closure
static int
procrange (void *item, void *data)
{
struct msgrange *mp = item;
struct mu_msgrange *mp = item;
struct action_closure *clos = data;
size_t i;
......@@ -336,12 +63,15 @@ procrange (void *item, void *data)
/* Apply ACTION to each message number from LIST. */
int
util_foreach_message (mu_list_t list, imap4d_message_action_t action,
util_foreach_message (mu_msgset_t msgset, imap4d_message_action_t action,
void *data)
{
mu_list_t list;
struct action_closure clos;
clos.action = action;
clos.data = data;
mu_msgset_get_list (msgset, &list);
return mu_list_foreach (list, procrange, &clos);
}
......