commit-mailutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[SCM] GNU Mailutils branch, master, updated. release-2.2-555-g37fc6c3


From: Sergey Poznyakoff
Subject: [SCM] GNU Mailutils branch, master, updated. release-2.2-555-g37fc6c3
Date: Mon, 26 Dec 2011 21:16:59 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU Mailutils".

http://git.savannah.gnu.org/cgit/mailutils.git/commit/?id=37fc6c3c41b16823e9b130a2a425ed40eebf09f8

The branch, master has been updated
       via  37fc6c3c41b16823e9b130a2a425ed40eebf09f8 (commit)
       via  0ff01881ca8dd159c2802751901644daef2bcc19 (commit)
       via  1c7d50a62de57fbb0552bcde955db928908b2a98 (commit)
       via  e072a2734eaf1bffdacfc20fcb598d79a50d11e7 (commit)
      from  d16a3de50d8c43f7b45f18db1704fa784ee4cb7b (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 37fc6c3c41b16823e9b130a2a425ed40eebf09f8
Author: Sergey Poznyakoff <address@hidden>
Date:   Mon Dec 26 23:11:00 2011 +0200

    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.

commit 0ff01881ca8dd159c2802751901644daef2bcc19
Author: Sergey Poznyakoff <address@hidden>
Date:   Mon Dec 26 22:13:52 2011 +0200

    Use mu_msgset_t in imap client for FETCH, STORE and COPY.
    
    * include/mailutils/imap.h (mu_imap_fetch,mu_imap_store)
    (mu_imap_store_flags,mu_imap_copy): Take mu_msgset_t as the
    message set parameter.
    * include/mailutils/imapio.h (mu_imapio_send_msgset): New proto.
    (mu_imapio_send_command_v,mu_imapio_send_command)
    (mu_imapio_send_command_e): Take additional mu_msgset_t parameter.
    All uses changed.
    * include/mailutils/msgset.h (mu_msgset_parse_imap): Input string
    is const.
    * include/mailutils/sys/imap.h (imap_command) <msgset>: New member.
    * libmailutils/imapio/sendmsgset.c: New file.
    * libmailutils/imapio/Makefile.am (libimapio_la_SOURCES): Add sendmsgset.c.
    * libmailutils/imapio/sendcmd.c (mu_imapio_send_command): Take additional
    mu_msgset_t parameter.
    * libmailutils/imapio/sendcmde.c (mu_imapio_send_command_e): Likewise.
    * libmailutils/imapio/sendcmdv.c (mu_imapio_send_command_v): Likewise.
    * libmailutils/msgset/parse.c (mu_msgset_parse_imap): Input string
    is const.
    * libproto/imap/copy.c (mu_imap_copy): Take mu_msgset_t as the
    message set parameter.
    * libproto/imap/fetch.c (mu_imap_fetch): Likewise.
    * libproto/imap/store.c (mu_imap_store): Likewise.
    * libproto/imap/storeflg.c (mu_imap_store_flags): Likewise.
    * libproto/imap/gencom.c (mu_imap_gencom): Send cmd->msgset.

commit 1c7d50a62de57fbb0552bcde955db928908b2a98
Author: Sergey Poznyakoff <address@hidden>
Date:   Mon Dec 26 20:34:43 2011 +0200

    Bugfix.
    
    * libmailutils/mailbox/mailbox.c (_MBOX_CHECK_FLAGS): Fix return code.

commit e072a2734eaf1bffdacfc20fcb598d79a50d11e7
Author: Sergey Poznyakoff <address@hidden>
Date:   Mon Dec 26 14:06:57 2011 +0200

    Add functions for operations on message sets.
    
    * configure.ac: Build libmailutils/msgset/Makefile
    * include/mailutils/msgset.h: New file.
    * include/mailutils/Makefile.am (pkginclude_HEADERS): Add msgset.h
    * include/mailutils/mailutils.h: Include mailutils/msgset.h
    * include/mailutils/sys/msgset.h: New file.
    * include/mailutils/sys/Makefile.am (sysinclude_HEADERS): Add msgset.h
    * include/mailutils/types.hin (mu_msgset_t): New type.
    * libmailutils/Makefile.am (SUBDIRS): Add msgset.
    (libmailutils_la_LIBADD): Add libmsgset.la
    * libmailutils/msgset/Makefile.am: New file.
    * libmailutils/msgset/add.c: New file.
    * libmailutils/msgset/aggr.c: New file.
    * libmailutils/msgset/clear.c: New file.
    * libmailutils/msgset/create.c: New file.
    * libmailutils/msgset/free.c: New file.
    * libmailutils/msgset/getitr.c: New file.
    * libmailutils/msgset/getlist.c: New file.
    * libmailutils/msgset/locate.c: New file.
    * libmailutils/msgset/parse.c: New file.
    * libmailutils/msgset/print.c: New file.
    * libmailutils/msgset/sub.c: New file.
    * libmailutils/tests/msgset.at: New file.
    * libmailutils/tests/msgset.c: New file.
    * libmailutils/tests/Makefile.am (noinst_PROGRAMS): Add msgset.
    (TESTSUITE_AT): Add msgset.at.
    * libmailutils/tests/testsuite.at: Include msgset.at.
    * testsuite/msgset.c: New file.
    * testsuite/Makefile.am (noinst_PROGRAMS): Add msgset.

-----------------------------------------------------------------------

Summary of changes:
 configure.ac                                       |    1 +
 imap4d/copy.c                                      |   34 ++-
 imap4d/fetch.c                                     |   20 +-
 imap4d/imap4d.h                                    |    6 +-
 imap4d/search.c                                    |   98 +++++---
 imap4d/store.c                                     |   18 +-
 imap4d/util.c                                      |  280 +-------------------
 include/mailutils/Makefile.am                      |    1 +
 include/mailutils/datetime.h                       |    8 +
 include/mailutils/imap.h                           |    8 +-
 include/mailutils/imapio.h                         |    6 +-
 include/mailutils/mailutils.h                      |    1 +
 include/mailutils/msgset.h                         |   61 +++++
 include/mailutils/sys/Makefile.am                  |    1 +
 include/mailutils/sys/imap.h                       |    1 +
 .../close.c => include/mailutils/sys/msgset.h      |   24 +-
 include/mailutils/types.hin                        |    4 +-
 libmailutils/Makefile.am                           |    3 +-
 libmailutils/imapio/Makefile.am                    |    1 +
 libmailutils/imapio/sendcmd.c                      |   13 +-
 libmailutils/imapio/sendcmde.c                     |   12 +-
 libmailutils/imapio/sendcmdv.c                     |   12 +-
 libmailutils/imapio/{send.c => sendmsgset.c}       |    6 +-
 libmailutils/mailbox/mailbox.c                     |    2 +-
 libmailutils/{sockaddr => msgset}/Makefile.am      |   21 +-
 libmailutils/{imapio/sendflg.c => msgset/add.c}    |   29 ++-
 libmailutils/msgset/aggr.c                         |  169 ++++++++++++
 libmailutils/{imapio/send.c => msgset/clear.c}     |   17 +-
 libmailutils/msgset/create.c                       |   60 +++++
 libmailutils/{imapio/time.c => msgset/free.c}      |   32 ++-
 libmailutils/{imapio/send.c => msgset/getitr.c}    |   15 +-
 libmailutils/{imapio/send.c => msgset/getlist.c}   |   18 +-
 libmailutils/{imapio/time.c => msgset/locate.c}    |   18 +-
 libmailutils/msgset/parse.c                        |  179 +++++++++++++
 libmailutils/msgset/print.c                        |   74 +++++
 libmailutils/msgset/sub.c                          |  233 ++++++++++++++++
 libmailutils/tests/Makefile.am                     |    2 +
 libmailutils/tests/msgset.at                       |  175 ++++++++++++
 libmailutils/tests/msgset.c                        |  125 +++++++++
 libmailutils/tests/testsuite.at                    |    2 +
 libproto/imap/capability.c                         |    2 +-
 libproto/imap/close.c                              |    1 +
 libproto/imap/copy.c                               |    5 +-
 libproto/imap/delete.c                             |    1 +
 libproto/imap/fetch.c                              |    5 +-
 libproto/imap/gencom.c                             |    1 +
 libproto/imap/genlist.c                            |    1 +
 libproto/imap/login.c                              |    2 +-
 libproto/imap/mbcreate.c                           |    3 +-
 libproto/imap/mbox.c                               |  206 ++++++++-------
 libproto/imap/rename.c                             |    1 +
 libproto/imap/select.c                             |    2 +-
 libproto/imap/store.c                              |    5 +-
 libproto/imap/storeflg.c                           |    7 +-
 libproto/imap/subscribe.c                          |    1 +
 libproto/imap/unselect.c                           |    1 +
 libproto/imap/unsubscribe.c                        |    1 +
 mu/imap.c                                          |   76 ++++--
 testsuite/Makefile.am                              |    1 +
 testsuite/msgset.c                                 |  135 ++++++++++
 60 files changed, 1683 insertions(+), 564 deletions(-)
 create mode 100644 include/mailutils/msgset.h
 copy libmu_dbm/close.c => include/mailutils/sys/msgset.h (70%)
 copy libmailutils/imapio/{send.c => sendmsgset.c} (86%)
 copy libmailutils/{sockaddr => msgset}/Makefile.am (85%)
 copy libmailutils/{imapio/sendflg.c => msgset/add.c} (62%)
 create mode 100644 libmailutils/msgset/aggr.c
 copy libmailutils/{imapio/send.c => msgset/clear.c} (76%)
 create mode 100644 libmailutils/msgset/create.c
 copy libmailutils/{imapio/time.c => msgset/free.c} (70%)
 copy libmailutils/{imapio/send.c => msgset/getitr.c} (74%)
 copy libmailutils/{imapio/send.c => msgset/getlist.c} (73%)
 copy libmailutils/{imapio/time.c => msgset/locate.c} (72%)
 create mode 100644 libmailutils/msgset/parse.c
 create mode 100644 libmailutils/msgset/print.c
 create mode 100644 libmailutils/msgset/sub.c
 create mode 100644 libmailutils/tests/msgset.at
 create mode 100644 libmailutils/tests/msgset.c
 create mode 100644 testsuite/msgset.c

diff --git a/configure.ac b/configure.ac
index 9472fb7..8f1cbeb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1479,6 +1479,7 @@ AC_CONFIG_FILES([
  libmailutils/mailbox/Makefile
  libmailutils/mailer/Makefile
  libmailutils/mime/Makefile
+ libmailutils/msgset/Makefile
  libmailutils/property/Makefile
  libmailutils/server/Makefile
  libmailutils/string/Makefile
diff --git a/imap4d/copy.c b/imap4d/copy.c
index 5e60487..f2c2314 100644
--- a/imap4d/copy.c
+++ b/imap4d/copy.c
@@ -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)
     {
diff --git a/imap4d/fetch.c b/imap4d/fetch.c
index ad42ff3..46cb835 100644
--- a/imap4d/fetch.c
+++ b/imap4d/fetch.c
@@ -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;
 }
 
diff --git a/imap4d/imap4d.h b/imap4d/imap4d.h
index f652c1a..22b3b28 100644
--- a/imap4d/imap4d.h
+++ b/imap4d/imap4d.h
@@ -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 */
diff --git a/imap4d/search.c b/imap4d/search.c
index 54c212e..2441810 100644
--- a/imap4d/search.c
+++ b/imap4d/search.c
@@ -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;
 }                      
diff --git a/imap4d/store.c b/imap4d/store.c
index 20318b5..0fe425d 100644
--- a/imap4d/store.c
+++ b/imap4d/store.c
@@ -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;
 }
diff --git a/imap4d/util.c b/imap4d/util.c
index 643d962..c5341de 100644
--- a/imap4d/util.c
+++ b/imap4d/util.c
@@ -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);
 }
 
diff --git a/include/mailutils/Makefile.am b/include/mailutils/Makefile.am
index 53ea5e4..d8e46ac 100644
--- a/include/mailutils/Makefile.am
+++ b/include/mailutils/Makefile.am
@@ -73,6 +73,7 @@ pkginclude_HEADERS = \
  mh.h\
  mime.h\
  monitor.h\
+ msgset.h\
  mu_auth.h\
  util.h\
  nls.h\
diff --git a/include/mailutils/datetime.h b/include/mailutils/datetime.h
index bc1aa98..dbf1447 100644
--- a/include/mailutils/datetime.h
+++ b/include/mailutils/datetime.h
@@ -18,6 +18,10 @@
 #ifndef _MAILUTILS_DATETIME_H
 #define _MAILUTILS_DATETIME_H
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #include <time.h>
 #include <mailutils/types.h>
 
@@ -83,4 +87,8 @@ int mu_scan_datetime (const char *input, const char *fmt, 
struct tm *tm,
 #define MU_DATETIME_FORM_RFC822  "%a, %e %b %Y %H:%M:%S %z"
 #define MU_DATETIME_SCAN_RFC822  "%[%a, %]%e %b %Y %H:%M%[:%S%] %z"
 
+#ifdef __cplusplus
+}
+#endif
+  
 #endif
diff --git a/include/mailutils/imap.h b/include/mailutils/imap.h
index 09dad70..200656c 100644
--- a/include/mailutils/imap.h
+++ b/include/mailutils/imap.h
@@ -66,9 +66,9 @@ int mu_imap_id (mu_imap_t imap, char **idenv, mu_assoc_t 
*passoc);
 int mu_imap_noop (mu_imap_t imap);
 int mu_imap_check (mu_imap_t imap);
 
-int mu_imap_fetch (mu_imap_t imap, int uid, const char *msgset,
+int mu_imap_fetch (mu_imap_t imap, int uid, mu_msgset_t msgset,
                   const char *items);
-int mu_imap_store (mu_imap_t imap, int uid, const char *msgset,
+int mu_imap_store (mu_imap_t imap, int uid, mu_msgset_t msgset,
                   const char *items);
 
 #define MU_IMAP_STORE_SET 0
@@ -78,13 +78,13 @@ int mu_imap_store (mu_imap_t imap, int uid, const char 
*msgset,
 
 #define MU_IMAP_STORE_OPMASK 0xf
 
-int mu_imap_store_flags (mu_imap_t imap, int uid, const char *msgset,
+int mu_imap_store_flags (mu_imap_t imap, int uid, mu_msgset_t msgset,
                         int op, int flags);
   
 int mu_imap_delete (mu_imap_t imap, const char *mailbox);
 int mu_imap_rename (mu_imap_t imap, const char *mailbox,
                    const char *new_mailbox);
-int mu_imap_copy (mu_imap_t imap, int uid, const char *msgset,
+int mu_imap_copy (mu_imap_t imap, int uid, mu_msgset_t msgset,
                  const char *mailbox);
 
 int mu_imap_close (mu_imap_t imap);
diff --git a/include/mailutils/imapio.h b/include/mailutils/imapio.h
index d038744..dbd5e5d 100644
--- a/include/mailutils/imapio.h
+++ b/include/mailutils/imapio.h
@@ -44,13 +44,15 @@ int mu_imapio_send_literal_stream (mu_imapio_t io, 
mu_stream_t stream,
 int mu_imapio_send_qstring (mu_imapio_t io, const char *buffer);
 int mu_imapio_send_qstring_unfold (mu_imapio_t io, const char *buffer,
                                   int unfold);
+int mu_imapio_send_msgset (mu_imapio_t io, mu_msgset_t msgset);
 
 int mu_imapio_send_command_v (mu_imapio_t io, const char *tag,
+                             mu_msgset_t msgset,
                              int argc, char const **argv, const char *extra);
 int mu_imapio_send_command (mu_imapio_t io, const char *tag,
-                       char const *cmd, ...);
+                           mu_msgset_t msgset, char const *cmd, ...);
 int mu_imapio_send_command_e (mu_imapio_t io, const char *tag,
-                             char const *cmd, ...);
+                             mu_msgset_t msgset, char const *cmd, ...);
   
 int mu_imapio_send_flags (mu_imapio_t io, int flags);
 int mu_imapio_send_time (mu_imapio_t io, struct tm *tm,
diff --git a/include/mailutils/mailutils.h b/include/mailutils/mailutils.h
index eb2a616..b562136 100644
--- a/include/mailutils/mailutils.h
+++ b/include/mailutils/mailutils.h
@@ -70,5 +70,6 @@
 #include <mailutils/prog.h>
 #include <mailutils/sockaddr.h>
 #include <mailutils/cidr.h>
+#include <mailutils/msgset.h>
 
 /* EOF */
diff --git a/include/mailutils/msgset.h b/include/mailutils/msgset.h
new file mode 100644
index 0000000..f9ae5b2
--- /dev/null
+++ b/include/mailutils/msgset.h
@@ -0,0 +1,61 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+   Copyright (C) 2011 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
+   <http://www.gnu.org/licenses/>. */
+
+#ifndef _MAILUTILS_MSGSET_H
+#define _MAILUTILS_MSGSET_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct mu_msgrange
+{
+  size_t msg_beg;
+  size_t msg_end;
+};
+
+/* Message numbers start with 1.  MU_MSGNO_LAST denotes the last
+   message. */
+#define MU_MSGNO_LAST   0
+  
+#define MU_MSGSET_UID   0x01   /* Message set operates on UIDs */
+  
+int mu_msgset_create (mu_msgset_t *pmsgset, mu_mailbox_t mbox, int flags);
+int mu_msgset_get_list (mu_msgset_t msgset, mu_list_t *plist);
+int mu_msgset_get_iterator (mu_msgset_t msgset, mu_iterator_t *pitr);
+
+int mu_msgset_add_range (mu_msgset_t list, size_t beg, size_t end);
+int mu_msgset_sub_range (mu_msgset_t list, size_t beg, size_t end);
+  /*int mu_msgset_add_set (mu_msgset_t a, mu_msgset_t b);*/
+  /*int mu_msgset_sub_set (mu_msgset_t a, mu_msgset_t b);*/
+int mu_msgset_aggregate (mu_msgset_t set);
+int mu_msgset_clear (mu_msgset_t set);
+void mu_msgset_free (mu_msgset_t set);
+void mu_msgset_destroy (mu_msgset_t *set);
+  
+int mu_msgset_parse_imap (mu_msgset_t set, const char *s, char **end);
+
+int mu_msgset_print (mu_stream_t str, mu_msgset_t msgset);
+  
+int mu_msgset_locate (mu_msgset_t msgset, size_t n,
+                     struct mu_msgrange const **prange);
+  
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/mailutils/sys/Makefile.am 
b/include/mailutils/sys/Makefile.am
index 86412d0..27e2fe4 100644
--- a/include/mailutils/sys/Makefile.am
+++ b/include/mailutils/sys/Makefile.am
@@ -44,6 +44,7 @@ sysinclude_HEADERS = \
  message.h\
  mime.h\
  monitor.h\
+ msgset.h\
  nntp.h\
  nullstream.h\
  observer.h\
diff --git a/include/mailutils/sys/imap.h b/include/mailutils/sys/imap.h
index fd0f905..995673a 100644
--- a/include/mailutils/sys/imap.h
+++ b/include/mailutils/sys/imap.h
@@ -151,6 +151,7 @@ struct imap_command
   int argc;
   char const **argv;
   char const *extra;
+  mu_msgset_t msgset;
   void (*tagged_handler) (mu_imap_t);
   mu_imap_response_action_t untagged_handler;
   void *untagged_handler_data;
diff --git a/libmu_dbm/close.c b/include/mailutils/sys/msgset.h
similarity index 70%
copy from libmu_dbm/close.c
copy to include/mailutils/sys/msgset.h
index a84574a..9ecb053 100644
--- a/libmu_dbm/close.c
+++ b/include/mailutils/sys/msgset.h
@@ -15,21 +15,17 @@
    Public License along with this library.  If not, see
    <http://www.gnu.org/licenses/>. */
 
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
+#ifndef _MAILUTILS_SYS_MSGSET_H
+# define _MAILUTILS_SYS_MSGSET_H
 
-#include <mailutils/types.h>
-#include <mailutils/dbm.h>
-#include <mailutils/errno.h>
-#include "mudbm.h"
+#define _MU_MSGSET_AGGREGATED 0x10
+#define _MU_MSGSET_USERFLAG_MASK 0x0f
 
-int
-mu_dbm_close (mu_dbm_file_t db)
+struct _mu_msgset
 {
-  DBMSYSCK (db, _dbm_close);
-  if (!db->db_descr)
-    return 0;
-  return db->db_sys->_dbm_close (db);
-}
+  mu_list_t list;           /* List of mu_msgrange structures */
+  mu_mailbox_t mbox;        /* Associated mailbox */
+  int flags;                /* Message set flags */
+};
 
+#endif
diff --git a/include/mailutils/types.hin b/include/mailutils/types.hin
index b98d617..bda2c37 100644
--- a/include/mailutils/types.hin
+++ b/include/mailutils/types.hin
@@ -77,7 +77,8 @@ struct _mu_server;
 struct _mu_tcp_server;
 struct _mu_dbm_file;
 struct _mu_imapio;
-  
+struct _mu_msgset;
+
 struct mu_sockaddr; /* defined in mailutils/sockaddr.h */  
 struct mu_cidr;     /* defined in mailutils/cidr.h */  
   
@@ -125,6 +126,7 @@ typedef struct _mu_secret *mu_secret_t;
 typedef struct _mu_mime_io_buffer *mu_mime_io_buffer_t;
 typedef struct _mu_dbm_file *mu_dbm_file_t;
 typedef struct _mu_imapio *mu_imapio_t;
+typedef struct _mu_msgset *mu_msgset_t;
 
 typedef void (*mu_onexit_t) (void*);
 typedef unsigned int mu_debug_handle_t;
diff --git a/libmailutils/Makefile.am b/libmailutils/Makefile.am
index c01b321..bbc8447 100644
--- a/libmailutils/Makefile.am
+++ b/libmailutils/Makefile.am
@@ -18,7 +18,7 @@
 
 SUBDIRS = \
  auth base address list sockaddr cidr cfg diag\
- filter mailbox mailer mime server string stream stdstream\
+ filter mailbox mailer mime msgset server string stream stdstream\
  property url imapio datetime . tests 
 
 lib_LTLIBRARIES = libmailutils.la
@@ -41,6 +41,7 @@ libmailutils_la_LIBADD = \
  mailbox/libmailbox.la\
  mailer/libmailer.la\
  mime/libmime.la\
+ msgset/libmsgset.la\
  property/libproperty.la\
  server/libserver.la\
  string/libstring.la\
diff --git a/libmailutils/imapio/Makefile.am b/libmailutils/imapio/Makefile.am
index beb590b..93644a9 100644
--- a/libmailutils/imapio/Makefile.am
+++ b/libmailutils/imapio/Makefile.am
@@ -32,6 +32,7 @@ libimapio_la_SOURCES = \
  sendcmdv.c\
  sendcmde.c\
  sendflg.c\
+ sendmsgset.c\
  time.c\
  trace.c\
  transport.c\
diff --git a/libmailutils/imapio/sendcmd.c b/libmailutils/imapio/sendcmd.c
index bf4c5f6..3d25a04 100644
--- a/libmailutils/imapio/sendcmd.c
+++ b/libmailutils/imapio/sendcmd.c
@@ -23,10 +23,14 @@
 
 /* Send a IMAP command to the server, quoting its arguments as necessary.
    TAG is the command tag, CMD is the command verb.  Command arguments are
-   given in variadic list terminated with NULL. */
+   given in variadic list terminated with NULL.
+
+   If MSGSET is supplied, it is sent when the function encounters the
+   "\\" (single backslash) in ARGV.
+*/
 int
 mu_imapio_send_command (struct _mu_imapio *io, const char *tag,
-                       char const *cmd, ...)
+                       mu_msgset_t msgset, char const *cmd, ...)
 {
   va_list ap;
   
@@ -35,7 +39,10 @@ mu_imapio_send_command (struct _mu_imapio *io, const char 
*tag,
   while ((cmd = va_arg (ap, char *)))
     {
       mu_imapio_send (io, " ", 1);
-      mu_imapio_send_qstring (io, cmd);
+      if (msgset && strcmp (cmd, "\\") == 0)
+       mu_imapio_send_msgset (io, msgset);
+      else
+       mu_imapio_send_qstring (io, cmd);
     }
   va_end (ap);
   mu_imapio_send (io, "\r\n", 2);
diff --git a/libmailutils/imapio/sendcmde.c b/libmailutils/imapio/sendcmde.c
index 1618c52..5069f6c 100644
--- a/libmailutils/imapio/sendcmde.c
+++ b/libmailutils/imapio/sendcmde.c
@@ -24,9 +24,12 @@
 /* Send a IMAP command to the server, quoting its arguments as necessary.
    TAG is the command tag, CMD is the command verb.  Command arguments are
    given in variadic list terminated with NULL. The last argument is sent
-   over the wire as is, without quoting. */
+   over the wire as is, without quoting. If MSGSET is supplied, it is sent
+   when the function encounters the "\\" (single backslash) in ARGV.
+*/
 int
 mu_imapio_send_command_e (struct _mu_imapio *io, const char *tag,
+                         mu_msgset_t msgset,
                          char const *cmd, ...)
 {
   va_list ap;
@@ -40,7 +43,12 @@ mu_imapio_send_command_e (struct _mu_imapio *io, const char 
*tag,
       
       mu_imapio_send (io, " ", 1);
       if (next)
-       mu_imapio_send_qstring (io, cmd);
+       {
+         if (msgset && strcmp (cmd, "\\") == 0)
+           mu_imapio_send_msgset (io, msgset);
+         else
+           mu_imapio_send_qstring (io, cmd);
+       }
       else
        mu_imapio_send (io, cmd, strlen (cmd));
       cmd = next;
diff --git a/libmailutils/imapio/sendcmdv.c b/libmailutils/imapio/sendcmdv.c
index af9fa8d..ef1bcc2 100644
--- a/libmailutils/imapio/sendcmdv.c
+++ b/libmailutils/imapio/sendcmdv.c
@@ -23,9 +23,14 @@
 /* Send a IMAP command to the server, quoting its arguments as necessary.
    TAG is the command tag, ARGV contains ARGC elements and supplies the
    command (in ARGV[0]) and its arguments. EXTRA (if not NULL) supplies
-   additional arguments that will be sent as is. */
+   additional arguments that will be sent as is.
+
+   If MSGSET is supplied, it is sent when the function encounters the
+   "\\" (single backslash) in ARGV.
+*/
 int
 mu_imapio_send_command_v (struct _mu_imapio *io, const char *tag,
+                         mu_msgset_t msgset,
                          int argc, char const **argv, const char *extra)
 {
   int i;
@@ -34,7 +39,10 @@ mu_imapio_send_command_v (struct _mu_imapio *io, const char 
*tag,
   for (i = 1; i < argc; i++)
     {
       mu_imapio_send (io, " ", 1);
-      mu_imapio_send_qstring (io, argv[i]);
+      if (msgset && strcmp (argv[i], "\\") == 0)
+       mu_imapio_send_msgset (io, msgset);
+      else
+       mu_imapio_send_qstring (io, argv[i]);
     }
   if (extra)
     {
diff --git a/libmailutils/imapio/send.c b/libmailutils/imapio/sendmsgset.c
similarity index 86%
copy from libmailutils/imapio/send.c
copy to libmailutils/imapio/sendmsgset.c
index 11ee049..7f6cd78 100644
--- a/libmailutils/imapio/send.c
+++ b/libmailutils/imapio/sendmsgset.c
@@ -18,12 +18,12 @@
 #include <stdarg.h>
 #include <mailutils/types.h>
 #include <mailutils/imapio.h>
+#include <mailutils/msgset.h>
 #include <mailutils/stream.h>
 #include <mailutils/sys/imapio.h>
 
 int
-mu_imapio_send (mu_imapio_t io, const char *buf, size_t bytes)
+mu_imapio_send_msgset (mu_imapio_t io, mu_msgset_t msgset)
 {
-  return mu_stream_write (io->_imap_stream, buf, bytes, NULL);
+  return mu_msgset_print (io->_imap_stream, msgset);
 }
-    
diff --git a/libmailutils/mailbox/mailbox.c b/libmailutils/mailbox/mailbox.c
index 990ae6a..68a4e5a 100644
--- a/libmailutils/mailbox/mailbox.c
+++ b/libmailutils/mailbox/mailbox.c
@@ -448,7 +448,7 @@ mu_mailbox_flush (mu_mailbox_t mbox, int expunge)
   if (mbox->flags & _MU_MAILBOX_REMOVED)       \
     return MU_ERR_MBX_REMOVED;                 \
   if (!(mbox->flags & _MU_MAILBOX_OPEN))       \
-    return _MU_MAILBOX_OPEN
+    return MU_ERR_NOT_OPEN
 
 #define _MBOX_CHECK(mbox,method)               \
   _MBOX_CHECK_FLAGS(mbox);                     \
diff --git a/libmailutils/sockaddr/Makefile.am b/libmailutils/msgset/Makefile.am
similarity index 85%
copy from libmailutils/sockaddr/Makefile.am
copy to libmailutils/msgset/Makefile.am
index dcfb184..398806d 100644
--- a/libmailutils/sockaddr/Makefile.am
+++ b/libmailutils/msgset/Makefile.am
@@ -15,17 +15,20 @@
 # Public License along with this library.  If not, see
 # <http://www.gnu.org/licenses/>.
 
-noinst_LTLIBRARIES = libsockaddr.la
+noinst_LTLIBRARIES = libmsgset.la
 
-libsockaddr_la_SOURCES = \
- copy.c\
+libmsgset_la_SOURCES = \
+ add.c\
+ aggr.c\
+ clear.c\
  create.c\
+ getitr.c\
+ getlist.c\
  free.c\
- fromnode.c\
- insert.c\
- ipaddr.c\
- str.c\
- unlink.c\
- url.c
+ locate.c\
+ parse.c\
+ print.c\
+ sub.c
 
 INCLUDES = @MU_LIB_COMMON_INCLUDES@ -I/libmailutils
+
diff --git a/libmailutils/imapio/sendflg.c b/libmailutils/msgset/add.c
similarity index 62%
copy from libmailutils/imapio/sendflg.c
copy to libmailutils/msgset/add.c
index 94cfe22..9d97707 100644
--- a/libmailutils/imapio/sendflg.c
+++ b/libmailutils/msgset/add.c
@@ -15,22 +15,29 @@
    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
+#include <stdlib.h>
 #include <mailutils/types.h>
-#include <mailutils/imapio.h>
-#include <mailutils/stream.h>
-#include <mailutils/imaputil.h>
-#include <mailutils/sys/imapio.h>
+#include <mailutils/errno.h>
+#include <mailutils/list.h>
+#include <mailutils/msgset.h>
+#include <mailutils/sys/msgset.h>
 
 int
-mu_imapio_send_flags (struct _mu_imapio *io, int flags)
+mu_msgset_add_range (mu_msgset_t mset, size_t beg, size_t end)
 {
   int rc;
-
-  rc = mu_stream_write (io->_imap_stream, "(", 1, NULL);
+  struct mu_msgrange *range;
+  
+  if (!mset || beg == 0)
+    return EINVAL;
+  range = calloc (1, sizeof (*range));
+  if (!range)
+    return ENOMEM;
+  range->msg_beg = beg;
+  range->msg_end = end;
+  rc = mu_list_append (mset->list, range);
   if (rc)
-    return rc;
-  rc = mu_imap_format_flags (io->_imap_stream, flags, io->_imap_server);
-  if (rc == 0)
-    rc = mu_stream_write (io->_imap_stream, ")", 1, NULL);
+    free (range);
+  mset->flags &= ~_MU_MSGSET_AGGREGATED;
   return rc;
 }
diff --git a/libmailutils/msgset/aggr.c b/libmailutils/msgset/aggr.c
new file mode 100644
index 0000000..a6fe7aa
--- /dev/null
+++ b/libmailutils/msgset/aggr.c
@@ -0,0 +1,169 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+   Copyright (C) 2011 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/>. */
+
+#include <config.h>
+#include <stdlib.h>
+#include <mailutils/types.h>
+#include <mailutils/errno.h>
+#include <mailutils/list.h>
+#include <mailutils/iterator.h>
+#include <mailutils/msgset.h>
+#include <mailutils/sys/msgset.h>
+
+/* Comparator function for sorting the message set list. */
+static int
+compare_msgrange (const void *a, const void *b)
+{
+  struct mu_msgrange const *sa = a;
+  struct mu_msgrange const *sb = b;
+  
+  if (sa->msg_end != sb->msg_end)
+    {
+      if (sa->msg_end == MU_MSGNO_LAST)
+       return 1;
+      if (sb->msg_end == MU_MSGNO_LAST)
+       return -1;
+    }
+  
+  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 0;
+
+  if (sa->msg_end < sb->msg_end)
+    return -1;
+  return 1;
+}
+
+int
+mu_msgset_aggregate (mu_msgset_t mset)
+{
+  int rc;
+  mu_iterator_t itr;
+  size_t count;
+  struct mu_msgrange *prev = NULL, *mr;
+  int dir;
+  
+  if (!mset)
+    return EINVAL;
+
+  if (mset->flags & _MU_MSGSET_AGGREGATED)
+    return 0; /* nothing to do */
+  
+  rc = mu_list_count (mset->list, &count);
+  if (rc)
+    return rc;
+  if (count < 2)
+    return 0;
+  
+  mu_list_sort (mset->list, compare_msgrange);
+  
+  rc = mu_list_get_iterator (mset->list, &itr);
+  if (rc)
+    return rc;
+  /* Set backward direction */
+  dir = 1;
+  rc = mu_iterator_ctl (itr, mu_itrctl_set_direction, &dir);
+  if (rc)
+    {
+      mu_iterator_destroy (&itr);
+      return rc;
+    }
+
+  mu_iterator_first (itr);
+  mu_iterator_current (itr, (void **)&mr);
+  
+  if (mr->msg_end == MU_MSGNO_LAST)
+    {
+      struct mu_msgrange *last = mr;
+      
+      for (mu_iterator_next (itr);
+          rc == 0 && !mu_iterator_is_done (itr); mu_iterator_next (itr))
+       {
+         mu_iterator_current (itr, (void **)&mr);
+         if (mr->msg_end == MU_MSGNO_LAST)
+           {
+             last->msg_beg = mr->msg_beg;
+             rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+           }
+         else if (mr->msg_beg >= last->msg_beg)
+           rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+         else if (mr->msg_end + 1 >= last->msg_beg)
+           {
+             last->msg_beg = mr->msg_beg;
+             rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+           }
+         else
+           break;
+       }
+    }
+
+  if (rc == 0)
+    {
+      dir = 0;
+      rc = mu_iterator_ctl (itr, mu_itrctl_set_direction, &dir);
+      if (rc)
+       {
+         mu_iterator_destroy (&itr);
+         return rc;
+       }
+
+      for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
+          mu_iterator_next (itr))
+       {
+         mu_iterator_current (itr, (void **)&mr);
+         if (mr->msg_end == MU_MSGNO_LAST)
+           break;
+         if (prev)
+           {
+             if (prev->msg_beg <= mr->msg_beg && mr->msg_beg <= prev->msg_end)
+               {
+                 /* Possible cases:
+                    1. mr lies fully within prev:
+                    mr->msg_end <= prev->msg_end
+                    Action: mr deleted
+                    2. mr overlaps with prev:
+                    mr->msg_end > prev->msg_end
+                    Action: prev->msg_end is set to mr->msg_end; mr deleted
+                 */
+                 if (mr->msg_end > prev->msg_end)
+                   prev->msg_end = mr->msg_end;
+                 rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+                 if (rc)
+                   break;
+                 continue;
+               }
+             else if (prev->msg_end + 1 == mr->msg_beg)
+               {
+                 prev->msg_end = mr->msg_end;
+                 rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+                 if (rc)
+                   break;
+                 continue;
+               }
+           }
+         prev = mr;
+       }
+    }
+  mu_iterator_destroy (&itr);
+
+  if (rc == 0)
+    mset->flags |= _MU_MSGSET_AGGREGATED;
+  return rc;
+}
diff --git a/libmailutils/imapio/send.c b/libmailutils/msgset/clear.c
similarity index 76%
copy from libmailutils/imapio/send.c
copy to libmailutils/msgset/clear.c
index 11ee049..f3f7ef8 100644
--- a/libmailutils/imapio/send.c
+++ b/libmailutils/msgset/clear.c
@@ -15,15 +15,18 @@
    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
-#include <stdarg.h>
 #include <mailutils/types.h>
-#include <mailutils/imapio.h>
-#include <mailutils/stream.h>
-#include <mailutils/sys/imapio.h>
+#include <mailutils/errno.h>
+#include <mailutils/list.h>
+#include <mailutils/msgset.h>
+#include <mailutils/sys/msgset.h>
 
 int
-mu_imapio_send (mu_imapio_t io, const char *buf, size_t bytes)
+mu_msgset_clear (mu_msgset_t mset)
 {
-  return mu_stream_write (io->_imap_stream, buf, bytes, NULL);
+  if (!mset)
+    return EINVAL;
+  mu_list_clear (mset->list);
+  return 0;
 }
-    
+
diff --git a/libmailutils/msgset/create.c b/libmailutils/msgset/create.c
new file mode 100644
index 0000000..0f70ab2
--- /dev/null
+++ b/libmailutils/msgset/create.c
@@ -0,0 +1,60 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+   Copyright (C) 2011 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/>. */
+
+#include <config.h>
+#include <stdlib.h>
+#include <mailutils/types.h>
+#include <mailutils/errno.h>
+#include <mailutils/list.h>
+#include <mailutils/msgset.h>
+#include <mailutils/sys/msgset.h>
+
+/* 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 mu_msgrange const *range = a;
+  size_t msgno = *(size_t*)b;
+
+  if (range->msg_beg <= msgno && msgno <= range->msg_end)
+    return 0;
+  return 1;
+}
+
+int
+mu_msgset_create (mu_msgset_t *pres, mu_mailbox_t mbox, int flags)
+{
+  mu_msgset_t msgset;
+  int rc;
+
+  msgset = calloc (1, sizeof (*msgset));
+  if (!msgset)
+    return ENOMEM;
+  
+  rc = mu_list_create (&msgset->list);
+  if (rc)
+    {
+      free (msgset);
+      return rc;
+    }
+  mu_list_set_destroy_item (msgset->list, mu_list_free_item);
+  mu_list_set_comparator (msgset->list, compare_msgnum);
+  msgset->mbox = mbox;
+  msgset->flags = flags & _MU_MSGSET_USERFLAG_MASK;
+  *pres = msgset;
+  return 0;
+}
diff --git a/libmailutils/imapio/time.c b/libmailutils/msgset/free.c
similarity index 70%
copy from libmailutils/imapio/time.c
copy to libmailutils/msgset/free.c
index 089a7db..77a348e 100644
--- a/libmailutils/imapio/time.c
+++ b/libmailutils/msgset/free.c
@@ -16,15 +16,27 @@
 
 #include <config.h>
 #include <stdlib.h>
-#include <mailutils/stream.h>
-#include <mailutils/errno.h>
-#include <mailutils/sys/imapio.h>
-#include <mailutils/datetime.h>
+#include <mailutils/list.h>
+#include <mailutils/msgset.h>
+#include <mailutils/sys/msgset.h>
 
-int
-mu_imapio_send_time (struct _mu_imapio *io, struct tm *tm,
-                    struct mu_timezone *tz)
+void
+mu_msgset_free (mu_msgset_t mset)
 {
-  return mu_c_streamftime (io->_imap_stream,
-                          "\"" MU_DATETIME_INTERNALDATE "\"", tm, tz);
-}  
+  if (mset)
+    {
+      mu_list_destroy (&mset->list);
+      free (mset);
+    }
+}
+
+void
+mu_msgset_destroy (mu_msgset_t *pset)
+{
+  if (pset)
+    {
+      mu_msgset_free (*pset);
+      *pset = NULL;
+    }
+}
+  
diff --git a/libmailutils/imapio/send.c b/libmailutils/msgset/getitr.c
similarity index 74%
copy from libmailutils/imapio/send.c
copy to libmailutils/msgset/getitr.c
index 11ee049..75cfb3d 100644
--- a/libmailutils/imapio/send.c
+++ b/libmailutils/msgset/getitr.c
@@ -15,15 +15,16 @@
    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
-#include <stdarg.h>
 #include <mailutils/types.h>
-#include <mailutils/imapio.h>
-#include <mailutils/stream.h>
-#include <mailutils/sys/imapio.h>
+#include <mailutils/errno.h>
+#include <mailutils/list.h>
+#include <mailutils/msgset.h>
+#include <mailutils/sys/msgset.h>
 
 int
-mu_imapio_send (mu_imapio_t io, const char *buf, size_t bytes)
+mu_msgset_get_iterator (mu_msgset_t msgset, mu_iterator_t *pitr)
 {
-  return mu_stream_write (io->_imap_stream, buf, bytes, NULL);
+  if (!msgset)
+    return EINVAL;
+  return mu_list_get_iterator (msgset->list, pitr);
 }
-    
diff --git a/libmailutils/imapio/send.c b/libmailutils/msgset/getlist.c
similarity index 73%
copy from libmailutils/imapio/send.c
copy to libmailutils/msgset/getlist.c
index 11ee049..5f7370d 100644
--- a/libmailutils/imapio/send.c
+++ b/libmailutils/msgset/getlist.c
@@ -15,15 +15,19 @@
    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
-#include <stdarg.h>
 #include <mailutils/types.h>
-#include <mailutils/imapio.h>
-#include <mailutils/stream.h>
-#include <mailutils/sys/imapio.h>
+#include <mailutils/errno.h>
+#include <mailutils/list.h>
+#include <mailutils/msgset.h>
+#include <mailutils/sys/msgset.h>
 
 int
-mu_imapio_send (mu_imapio_t io, const char *buf, size_t bytes)
+mu_msgset_get_list (mu_msgset_t msgset, mu_list_t *plist)
 {
-  return mu_stream_write (io->_imap_stream, buf, bytes, NULL);
+  if (!msgset)
+    return EINVAL;
+  if (!plist)
+    return MU_ERR_OUT_PTR_NULL;
+  *plist = msgset->list;
+  return 0;
 }
-    
diff --git a/libmailutils/imapio/time.c b/libmailutils/msgset/locate.c
similarity index 72%
copy from libmailutils/imapio/time.c
copy to libmailutils/msgset/locate.c
index 089a7db..7b94ece 100644
--- a/libmailutils/imapio/time.c
+++ b/libmailutils/msgset/locate.c
@@ -16,15 +16,17 @@
 
 #include <config.h>
 #include <stdlib.h>
-#include <mailutils/stream.h>
+#include <mailutils/types.h>
 #include <mailutils/errno.h>
-#include <mailutils/sys/imapio.h>
-#include <mailutils/datetime.h>
+#include <mailutils/list.h>
+#include <mailutils/msgset.h>
+#include <mailutils/sys/msgset.h>
 
 int
-mu_imapio_send_time (struct _mu_imapio *io, struct tm *tm,
-                    struct mu_timezone *tz)
+mu_msgset_locate (mu_msgset_t msgset, size_t n,
+                 struct mu_msgrange const **prange)
 {
-  return mu_c_streamftime (io->_imap_stream,
-                          "\"" MU_DATETIME_INTERNALDATE "\"", tm, tz);
-}  
+  if (!msgset || n == 0)
+    return EINVAL;
+  return mu_list_locate (msgset->list, &n, (void**)prange);
+}
diff --git a/libmailutils/msgset/parse.c b/libmailutils/msgset/parse.c
new file mode 100644
index 0000000..c522478
--- /dev/null
+++ b/libmailutils/msgset/parse.c
@@ -0,0 +1,179 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+   Copyright (C) 2011 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/>. */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <mailutils/types.h>
+#include <mailutils/errno.h>
+#include <mailutils/list.h>
+#include <mailutils/iterator.h>
+#include <mailutils/mailbox.h>
+#include <mailutils/msgset.h>
+#include <mailutils/sys/msgset.h>
+
+/* This structure keeps parser state while parsing message set. */
+struct parse_msgnum_env
+{
+  const char *s;         /* Current position in string */
+  size_t minval;         /* Min. sequence number or UID */
+  size_t maxval;         /* Max. sequence number or UID */
+  mu_msgset_t msgset;    /* Message set being built. */
+};
+
+/* 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 (env->msgset->mbox && env->maxval && 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 mu_msgrange msgrange;
+  
+  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_end < msgrange.msg_beg)
+    {
+      size_t tmp = msgrange.msg_end;
+      msgrange.msg_end = msgrange.msg_beg;
+      msgrange.msg_beg = tmp;
+    }
+
+  if ((env->msgset->flags & MU_MSGSET_UID) && env->msgset->mbox)
+    {
+      int rc;
+
+      rc = mu_mailbox_translate (env->msgset->mbox,
+                                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->msgset->mbox,
+                                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;
+    }      
+
+  return mu_msgset_add_range (env->msgset, msgrange.msg_beg, msgrange.msg_end);
+}
+
+/* Parse IMAP-style message set specification S.
+
+   On success, return 0 and populate MSET with the resulting message ranges.
+   On error, return error code and point END to the position in the input
+   string where parsing has failed. */
+int
+mu_msgset_parse_imap (mu_msgset_t mset, const char *s, char **end)
+{
+  int rc;
+  struct parse_msgnum_env env;
+  
+  if (!s || !mset)
+    return EINVAL;
+  if (!*s)
+    return MU_ERR_PARSE;
+
+  memset (&env, 0, sizeof (env));
+  env.s = s;
+  env.msgset = mset;
+  env.minval = 1;
+  
+  if (end)
+    *end = (char*) s;
+  if (mset->mbox)
+    {
+      size_t lastmsgno;      /* Max. sequence number. */
+
+      rc = mu_mailbox_messages_count (mset->mbox, &lastmsgno);
+      if (rc == 0)
+       {
+         if (mset->flags & MU_MSGSET_UID)
+           {
+             rc = mu_mailbox_translate (mset->mbox, MU_MAILBOX_MSGNO_TO_UID,
+                                        lastmsgno, &env.maxval);
+             if (rc == 0)
+               rc = mu_mailbox_translate (mset->mbox, MU_MAILBOX_MSGNO_TO_UID,
+                                          1, &env.minval);
+           }
+         else
+           env.maxval = lastmsgno;
+       }
+      if (rc)
+       return rc;
+    }
+  
+  while ((rc = parse_msgrange (&env)) == 0 && *env.s)
+    {
+      if (*env.s != ',' || *++env.s == 0)
+       {
+         rc = MU_ERR_PARSE;
+         break;
+       }
+    }
+
+  if (end)
+    *end = (char*) env.s;
+  return rc;
+}
diff --git a/libmailutils/msgset/print.c b/libmailutils/msgset/print.c
new file mode 100644
index 0000000..e7f990e
--- /dev/null
+++ b/libmailutils/msgset/print.c
@@ -0,0 +1,74 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+   Copyright (C) 2011 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/>. */
+
+#include <config.h>
+#include <stdlib.h>
+#include <mailutils/types.h>
+#include <mailutils/errno.h>
+#include <mailutils/list.h>
+#include <mailutils/stream.h>
+#include <mailutils/io.h>
+#include <mailutils/msgset.h>
+#include <mailutils/sys/msgset.h>
+
+struct print_env
+{
+  mu_stream_t stream;
+  int cont;
+};
+
+static int
+_msgrange_printer (void *item, void *data)
+{
+  int rc;
+  struct mu_msgrange *range = item;
+  struct print_env *env = data;
+
+  if (env->cont)
+    {
+      rc = mu_stream_write (env->stream, ",", 1, NULL);
+      if (rc)
+       return rc;
+    }
+  else
+    env->cont = 1;
+  if (range->msg_beg == range->msg_end)
+    rc = mu_stream_printf (env->stream, "%lu", (unsigned long) range->msg_beg);
+  else if (range->msg_end == 0)
+    rc = mu_stream_printf (env->stream, "%lu:*",
+                          (unsigned long) range->msg_beg);
+  else
+    rc = mu_stream_printf (env->stream, "%lu:%lu",
+                          (unsigned long) range->msg_beg,
+                          (unsigned long) range->msg_end);
+  return rc;
+}
+
+int
+mu_msgset_print (mu_stream_t str, mu_msgset_t mset)
+{
+  struct print_env env;
+  int rc;
+  
+  if (mu_list_is_empty (mset->list))
+    return MU_ERR_NOENT;
+  rc = mu_msgset_aggregate (mset);
+  if (rc)
+    return rc;
+  env.stream = str;
+  env.cont = 0;
+  return mu_list_foreach (mset->list, _msgrange_printer, &env);
+}
diff --git a/libmailutils/msgset/sub.c b/libmailutils/msgset/sub.c
new file mode 100644
index 0000000..2840781
--- /dev/null
+++ b/libmailutils/msgset/sub.c
@@ -0,0 +1,233 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+   Copyright (C) 2011 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/>. */
+
+#include <config.h>
+#include <stdlib.h>
+#include <mailutils/types.h>
+#include <mailutils/errno.h>
+#include <mailutils/list.h>
+#include <mailutils/iterator.h>
+#include <mailutils/msgset.h>
+#include <mailutils/sys/msgset.h>
+
+/* Remove range R from the set S.
+
+   There are following basic cases:
+
+   1. S does not contain R.
+
+      Return imeediately, there is nothing to do here.
+
+   2. S contains a range X that matches exactly R.
+
+      Action: Remove X entirely and return.
+
+   3. There is a range X in S such that R falls entirely within it.
+
+      Action: Split X into two ranges:
+
+         X.msg_beg   : R.msg_beg-1
+        R.msg_end+1 : X.msg_end
+
+      and return.
+      In border cases one of the new ranges may be empty.
+
+   4. There is a range X which contains an initial subrange of R.
+
+      Action: Remove the subrange from X. If the resulting range is
+      empty, remove it. Continue iteration with X.msg_end:R.msg_end
+      in place of R.
+
+   5. There is a range X such that its initial subrange is contained
+      in R.
+
+      Action: Remove X.msg_beg:R.msg_end and return.
+
+   6. There is a range X that is contained within R.
+
+      Action: Remove X and continue.
+
+There are two special cases.  First is when R.msg_end is 0, meaning the last
+available message number in the mailbox.  In this case the problem is reduced
+to either 2 or a combination of 4 and zero or more 6.  It is handled by the
+sub_msgno_last function below.
+
+Yet another special case is when the last element of S has msg_end == 0.  If
+so, the problem is reduced to either 2 or 4.
+*/
+
+static int
+sub_msgno_last (mu_msgset_t mset, size_t beg)
+{
+  int rc;
+  struct mu_msgrange *range;
+
+  if (beg == 1)
+    mu_list_clear (mset->list);
+  else
+    {
+      mu_iterator_t itr;
+      
+      rc = mu_list_get_iterator (mset->list, &itr);
+      if (rc)
+       return rc;
+      rc = 1;
+      rc = mu_iterator_ctl (itr, mu_itrctl_set_direction, &rc);
+      if (rc)
+       {
+         mu_iterator_destroy (&itr);
+         return rc;
+       }
+         
+      for (mu_iterator_first (itr); rc == 0 && !mu_iterator_is_done (itr);
+          mu_iterator_next (itr))
+       {
+         mu_iterator_current (itr, (void **)&range);
+         if (range->msg_beg > beg)
+           rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+         else if (range->msg_beg == beg)
+           {
+             rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+             break;
+           }
+         else
+           break;
+       }
+      mu_iterator_destroy (&itr);
+    }
+  return 0;
+}
+
+int
+mu_msgset_sub_range (mu_msgset_t mset, size_t beg, size_t end)
+{
+  int rc;
+  mu_iterator_t itr;
+  struct mu_msgrange *mr;
+
+  if (!mset)
+    return EINVAL;
+  if (mu_list_is_empty (mset->list))
+    return MU_ERR_NOENT;
+  rc = mu_msgset_aggregate (mset);
+  if (rc)
+    return rc;
+
+  if (end == MU_MSGNO_LAST)
+    return sub_msgno_last (mset, beg);
+  
+  /* Test border cases */
+  rc = mu_list_head (mset->list, (void**)&mr);
+  if (rc)
+    return rc;
+  if (end < mr->msg_beg)
+    return 0;
+  if (beg < mr->msg_beg)
+    beg = mr->msg_beg;
+  
+  if (rc)
+    return rc;
+  rc = mu_list_tail (mset->list, (void**) &mr);
+  if (mr->msg_end != MU_MSGNO_LAST)
+    {
+      if (beg > mr->msg_end)
+       return 0;
+      if (end > mr->msg_end)
+       end = mr->msg_end;
+    }
+  
+  rc = mu_list_get_iterator (mset->list, &itr);
+  if (rc)
+    return rc;
+  for (mu_iterator_first (itr); rc == 0 && !mu_iterator_is_done (itr);
+       mu_iterator_next (itr))
+    {
+      mu_iterator_current (itr, (void **)&mr);
+
+      if (mr->msg_end == MU_MSGNO_LAST)
+       {
+         /* This is the last element in list. */
+         if (mr->msg_beg == beg)
+           rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+         else if (mr->msg_beg > beg)
+           mr->msg_beg = end + 1;
+         break;
+       }
+      
+      if (mr->msg_beg == beg && mr->msg_end == end) /* See case 2 above */
+       {
+         rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+         break;
+       }
+      else if (mr->msg_beg <= beg && beg <= mr->msg_end) 
+       { 
+         if (mr->msg_beg <= end && end <= mr->msg_end) /* Case 3 */
+           {
+             /* Split the range */
+             if (end != mr->msg_end)
+               {
+                 struct mu_msgrange *newrange = calloc (1,
+                                                        sizeof (*newrange));
+                 if (!newrange)
+                   {
+                     rc = ENOMEM;
+                     break;
+                   }
+                 newrange->msg_beg = end + 1;
+                 newrange->msg_end = mr->msg_end;
+                 rc = mu_iterator_ctl (itr, mu_itrctl_insert, newrange);
+                 if (rc)
+                   {
+                     free (newrange);
+                     break;
+                   }
+               }
+             
+             if (mr->msg_beg == beg)
+               rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+             else
+               mr->msg_end = beg - 1;
+             break;
+           }
+         else if (mr->msg_beg == beg)      /* Case 4 */
+           {
+             beg = mr->msg_end;
+             rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+           }
+         else
+           {
+             size_t n = mr->msg_end;
+             mr->msg_end = beg - 1;
+             beg = n;
+           }
+       }
+      else if (mr->msg_beg <= end && end <= mr->msg_end) /* Case 5 */
+       {
+         mr->msg_beg = end + 1;
+         if (mr->msg_beg >= mr->msg_end)
+           rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+         break;
+       }
+      else if (beg <= mr->msg_beg && mr->msg_beg <= end)
+       {
+         rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+       }
+    }
+  mu_iterator_destroy (&itr);
+  
+  return rc;
+}
+      
diff --git a/libmailutils/tests/Makefile.am b/libmailutils/tests/Makefile.am
index 8b97c86..aa1661d 100644
--- a/libmailutils/tests/Makefile.am
+++ b/libmailutils/tests/Makefile.am
@@ -52,6 +52,7 @@ noinst_PROGRAMS = \
  listop\
  mailcap\
  mimehdr\
+ msgset\
  prop\
  scantime\
  strftime\
@@ -90,6 +91,7 @@ TESTSUITE_AT = \
  list.at\
  mailcap.at\
  mimehdr.at\
+ msgset.at\
  prop.at\
  scantime.at\
  strftime.at\
diff --git a/libmailutils/tests/msgset.at b/libmailutils/tests/msgset.at
new file mode 100644
index 0000000..a591afd
--- /dev/null
+++ b/libmailutils/tests/msgset.at
@@ -0,0 +1,175 @@
+# This file is part of GNU Mailutils. -*- Autotest -*-
+# Copyright (C) 2011 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([Message sets])
+
+dnl ------------------------------------------------------------------
+dnl MSGSET([NAME], [KW = `'], [OP], [STDOUT = `'], [STDERR = `'])
+dnl
+m4_pushdef([MSGSET],[
+AT_SETUP([$1])
+AT_KEYWORDS([msgset $2])
+AT_CHECK([msgset $3],[0],[$4],[$5])
+AT_CLEANUP
+])
+dnl ------------------------------------------------------------------
+
+MSGSET([Aggregation: simple],[msgset-aggr-simple msgset-aggr],
+[-msgset='1,5:11,20:24,80:90,22:30,50:60,4:12,41:50,70:81'],
+[1,4:12,20:30,41:60,70:90
+])
+
+MSGSET([Aggregation: open range (1)],
+[msgset-aggr-open-1 msgset-aggr-open msgset-aggr],
+[-msgset='12:*,1'],
+[1,12:*
+])
+
+MSGSET([Aggregation: coalescing open ranges],
+[msgset-aggr-open-2 msgset-aggr-open msgset-aggr],
+[-msgset='12:*,1,15:*,8:*'],
+[1,8:*
+])
+
+MSGSET([Aggregation: open range (3)],
+[msgset-aggr-open-3 msgset-aggr-open msgset-aggr],
+[-msgset='1,12:*,12:13'],
+[1,12:*
+])
+
+MSGSET([Aggregation: open range (4)],
+[msgset-aggr-open-4 msgset-aggr-open msgset-aggr],
+[-msgset='1,12:*,13:40'],
+[1,12:*
+])
+
+MSGSET([Aggregation: open range (5)],
+[msgset-aggr-open-5 msgset-aggr-open msgset-aggr],
+[-msgset='10:*,3,5:9'],
+[3,5:*
+])
+
+MSGSET([Create simple set],[msgset-simple msgset-add],
+[-add=1:10],
+[1:10
+])
+
+MSGSET([Create complex set],[msgset-complex msgset-add],
+[-add=1 -add=2 -add=3 -add=4 -add=10:20 -add=15:35 -add=36:40],
+[1:4,10:40
+])
+
+MSGSET([Subtract: no match],[msgset-sub msgset-sub-1],
+[-msgset=20:40 -sub=1:10],
+[20:40
+])
+
+MSGSET([Subtract: exact match],[msgset-sub msgset-sub-2],
+[-msgset=1,4:9,11 -sub=4:9],
+[1,11
+])
+
+MSGSET([Subtract: contained range],[msgset-sub msgset-sub-3],
+[-msgset=1:40 -sub=5:15
+],
+[1:4,16:40
+])
+
+MSGSET([Subtract: contained range (left border case)],
+[msgset-sub msgset-sub-3 msgset-sub-3-0],
+[-msgset=1:40 -sub=1:20],
+[21:40
+])
+
+MSGSET([Subtract: contained range (right border case)],
+[msgset-sub msgset-sub-3 msgset-sub-3-1],
+[-msgset=1:40 -sub=30:40],
+[1:29
+])
+
+MSGSET([Subtract: initial subrange],[msgset-sub msgset-sub-4],
+[-msgset=1:40,50:60,80:200 -sub=55:65],
+[1:40,50:54,80:200
+])
+
+MSGSET([Subtract: trailing subrange],[msgset-sub msgset-sub-5],
+[-msgset=1:40,50:60,80:200 -sub=45:55],
+[1:40,56:60,80:200
+])
+
+MSGSET([Subtract: overlapping subrange],[msgset-sub msgset-sub-6],
+[-msgset=1:40,50:60,80:200 -sub=41:70],
+[1:40,80:200
+])
+
+MSGSET([Subtract: 4, 5 and 6 combined],
+[msgset-sub msgset-sub-4 msgset-sub-5 msgset-sub-6 msgset-sub-456],
+[-msgset=1:40,50:60,80:200 -sub=30:100],
+[1:29,101:200
+])
+
+MSGSET([open range],[msgset-inf],
+[-msgset='1:*'],
+[1:*
+])
+
+MSGSET([add to open range],[msgset-inf-add msgset-add],
+[-msgset='10:*' -add=3 -add=5:9],
+[3,5:*
+])
+
+MSGSET([subtract from open range],[msgset-inf-sub msgset-sub],
+[-msgset='3,10:*' -sub=5:11],
+[3,12:*
+])
+
+MSGSET([subtract from open range an equal range],[msgset-inf-sub-1 msgset-sub],
+[-msgset='3,10:*' -sub=10:*],
+[3
+])
+
+MSGSET([subtract from open range a broader range],
+[msgset-inf-sub-2 msgset-sub],
+[-msgset='3,10:*' -sub=20:*],
+[3,10:*
+])
+
+MSGSET([subtract from open range a narrower range],
+[msgset-inf-sub-3 msgset-sub],
+[-msgset='3,10:*' -sub=5:*],
+[3
+])
+
+MSGSET([subtract an open range with matching left boundary],
+[msgset-inf-sub-4 msgset-sub],
+[-msgset='3,10:20' -sub=10:*],
+[3
+])
+
+MSGSET([subtract an open range with greater left boundary],
+[msgset-inf-sub-4 msgset-sub],
+[-msgset='3,10:20' -sub=11:*],
+[3,10:20
+])
+
+MSGSET([subtract an open range with smaller left boundary],
+[msgset-inf-sub-4 msgset-sub],
+[-msgset='3,10:20' -sub=8:*],
+[3
+])
+
+dnl ------------------------------------------------------------------
+m4_popdef([MSGSET])
\ No newline at end of file
diff --git a/libmailutils/tests/msgset.c b/libmailutils/tests/msgset.c
new file mode 100644
index 0000000..91db029
--- /dev/null
+++ b/libmailutils/tests/msgset.c
@@ -0,0 +1,125 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+   Copyright (C) 2011 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/>. */
+
+#include <config.h>
+#include <stdlib.h>
+#include <mailutils/mailutils.h>
+
+static void
+parse_msgset (char *arg, struct mu_msgrange *range)
+{
+  size_t msgnum;
+  char *p;
+  
+  errno = 0;
+  msgnum = strtoul (arg, &p, 10);
+  range->msg_beg = msgnum;
+  if (*p == ':')
+    {
+      if (*++p == '*')
+       msgnum = 0;
+      else
+       {
+         msgnum = strtoul (p, &p, 10);
+         if (*p)
+           {
+             mu_error ("error in message range near %s", p);
+             exit (1);
+           }
+       }
+    }
+  else if (*p == '*')
+    msgnum = 0;
+  else if (*p)
+    {
+      mu_error ("error in message range near %s", p);
+      exit (1);
+    }
+
+  range->msg_end = msgnum;
+}
+
+int
+main (int argc, char **argv)
+{
+  int i;
+  char *msgset_string = NULL;
+  mu_msgset_t msgset;
+  int rc;
+  
+  mu_set_program_name (argv[0]);
+  for (i = 1; i < argc; i++)
+    {
+      char *arg = argv[i];
+
+      if (strcmp (arg, "-h") == 0 || strcmp (arg, "-help") == 0)
+       {
+         mu_printf ("usage: %s [-msgset=SET] [-add X[:Y]] [-del X:[Y]]...\n",
+                    mu_program_name);
+         return 0;
+       }
+      else if (strncmp (arg, "-msgset=", 8) == 0)
+       msgset_string = arg + 8;
+      else
+       break;
+    }
+
+  MU_ASSERT (mu_msgset_create (&msgset, NULL, 0));
+  if (msgset_string)
+    {
+      char *end;
+      rc = mu_msgset_parse_imap (msgset, msgset_string, &end);
+      if (rc)
+       {
+         mu_error ("mu_msgset_parse_imap: %s near %s",
+                   mu_strerror (rc), end);
+         return 1;
+       }
+    }
+  
+  for (; i < argc; i++)
+    {
+      char *arg = argv[i];
+      struct mu_msgrange range;
+      
+      if (strncmp (arg, "-add=", 5) == 0)
+       {
+         parse_msgset (arg + 5, &range);
+         MU_ASSERT (mu_msgset_add_range (msgset, range.msg_beg,
+                                         range.msg_end));
+       }
+      else if (strncmp (arg, "-sub=", 5) == 0)
+       {
+         parse_msgset (arg + 5, &range);
+         MU_ASSERT (mu_msgset_sub_range (msgset, range.msg_beg,
+                                         range.msg_end));
+       }
+      else
+       {
+         mu_error ("unknown option %s", arg);
+         return 1;
+       }
+    }
+  MU_ASSERT (mu_msgset_print (mu_strout, msgset));
+  mu_printf ("\n");
+  mu_msgset_free (msgset);
+  
+  return 0;
+}
+  
+            
+
+       
diff --git a/libmailutils/tests/testsuite.at b/libmailutils/tests/testsuite.at
index b9b889c..15226c6 100644
--- a/libmailutils/tests/testsuite.at
+++ b/libmailutils/tests/testsuite.at
@@ -99,3 +99,5 @@ m4_include([strftime.at])
 m4_include([fsaf.at])
 
 m4_include([mimehdr.at])
+
+m4_include([msgset.at])
diff --git a/libproto/imap/capability.c b/libproto/imap/capability.c
index bc3c8a3..ade9a61 100644
--- a/libproto/imap/capability.c
+++ b/libproto/imap/capability.c
@@ -102,7 +102,7 @@ mu_imap_capability (mu_imap_t imap, int reread, 
mu_iterator_t *piter)
     case MU_IMAP_CLIENT_READY:
       status = _mu_imap_tag_next (imap);
       MU_IMAP_CHECK_EAGAIN (imap, status);
-      status = mu_imapio_send_command (imap->io, imap->tag_str,
+      status = mu_imapio_send_command (imap->io, imap->tag_str, NULL,
                                       "CAPABILITY", NULL); 
       MU_IMAP_CHECK_EAGAIN (imap, status);
       MU_IMAP_FCLR (imap, MU_IMAP_RESP);
diff --git a/libproto/imap/close.c b/libproto/imap/close.c
index 50bf1c5..17cd8cc 100644
--- a/libproto/imap/close.c
+++ b/libproto/imap/close.c
@@ -40,6 +40,7 @@ mu_imap_close (mu_imap_t imap)
     1,
     &command,
     NULL,
+    NULL,
     _mu_close_handler
   };
   return mu_imap_gencom (imap, &com);
diff --git a/libproto/imap/copy.c b/libproto/imap/copy.c
index 5598c20..e97990d 100644
--- a/libproto/imap/copy.c
+++ b/libproto/imap/copy.c
@@ -24,7 +24,7 @@
 #include <mailutils/sys/imap.h>
 
 int
-mu_imap_copy (mu_imap_t imap, int uid, const char *msgset, const char *mailbox)
+mu_imap_copy (mu_imap_t imap, int uid, mu_msgset_t msgset, const char *mailbox)
 {
   char const *argv[4];
   int i;
@@ -34,7 +34,7 @@ mu_imap_copy (mu_imap_t imap, int uid, const char *msgset, 
const char *mailbox)
   if (uid)
     argv[i++] = "UID";
   argv[i++] = "COPY";
-  argv[i++] = msgset;
+  argv[i++] = "\\";
   argv[i++] = mailbox;
 
   com.session_state = MU_IMAP_SESSION_SELECTED;
@@ -43,6 +43,7 @@ mu_imap_copy (mu_imap_t imap, int uid, const char *msgset, 
const char *mailbox)
   com.argc = i;
   com.argv = argv;
   com.extra = NULL;
+  com.msgset = msgset;
   com.tagged_handler = NULL;
   com.untagged_handler = NULL;
 
diff --git a/libproto/imap/delete.c b/libproto/imap/delete.c
index 5a605d0..dd4a45c 100644
--- a/libproto/imap/delete.c
+++ b/libproto/imap/delete.c
@@ -43,6 +43,7 @@ mu_imap_delete (mu_imap_t imap, const char *mailbox)
   com.argc = 2;
   com.argv = argv;
   com.extra = NULL;
+  com.msgset = NULL;
   com.tagged_handler = NULL;
   com.untagged_handler = NULL;
 
diff --git a/libproto/imap/fetch.c b/libproto/imap/fetch.c
index 22937df..471ad86 100644
--- a/libproto/imap/fetch.c
+++ b/libproto/imap/fetch.c
@@ -32,7 +32,7 @@
 #include <mailutils/sys/imap.h>
 
 int
-mu_imap_fetch (mu_imap_t imap, int uid, const char *msgset, const char *items)
+mu_imap_fetch (mu_imap_t imap, int uid, mu_msgset_t msgset, const char *items)
 {
   char const *argv[3];
   int i;
@@ -42,7 +42,7 @@ mu_imap_fetch (mu_imap_t imap, int uid, const char *msgset, 
const char *items)
   if (uid)
     argv[i++] = "UID";
   argv[i++] = "FETCH";
-  argv[i++] = msgset;
+  argv[i++] = "\\";
   
   com.session_state = MU_IMAP_SESSION_SELECTED;
   com.capa = NULL;
@@ -50,6 +50,7 @@ mu_imap_fetch (mu_imap_t imap, int uid, const char *msgset, 
const char *items)
   com.argc = i;
   com.argv = argv;
   com.extra = items;
+  com.msgset = msgset;
   com.tagged_handler = NULL;
   com.untagged_handler = NULL;
 
diff --git a/libproto/imap/gencom.c b/libproto/imap/gencom.c
index 57e8643..d0d8914 100644
--- a/libproto/imap/gencom.c
+++ b/libproto/imap/gencom.c
@@ -57,6 +57,7 @@ mu_imap_gencom (mu_imap_t imap, struct imap_command *cmd)
       status = _mu_imap_tag_next (imap);
       MU_IMAP_CHECK_EAGAIN (imap, status);
       status = mu_imapio_send_command_v (imap->io, imap->tag_str,
+                                        cmd->msgset,
                                         cmd->argc, cmd->argv, cmd->extra);
       MU_IMAP_CHECK_ERROR (imap, status);
       MU_IMAP_FCLR (imap, MU_IMAP_RESP);
diff --git a/libproto/imap/genlist.c b/libproto/imap/genlist.c
index 4feeb1c..7659d41 100644
--- a/libproto/imap/genlist.c
+++ b/libproto/imap/genlist.c
@@ -150,6 +150,7 @@ mu_imap_genlist (mu_imap_t imap, int lsub,
   com.argc = 3;
   com.argv = argv;
   com.extra = NULL;
+  com.msgset = NULL;
   com.tagged_handler = NULL;
   com.untagged_handler = list_untagged_handler;
   com.untagged_handler_data = &clos;
diff --git a/libproto/imap/login.c b/libproto/imap/login.c
index efdfb4b..e7220e4 100644
--- a/libproto/imap/login.c
+++ b/libproto/imap/login.c
@@ -44,7 +44,7 @@ mu_imap_login (mu_imap_t imap, const char *user, const char 
*pass)
        _mu_imap_xscript_level (imap, MU_XSCRIPT_SECURE);
       status = _mu_imap_tag_next (imap);
       MU_IMAP_CHECK_EAGAIN (imap, status);
-      status = mu_imapio_send_command (imap->io, imap->tag_str,
+      status = mu_imapio_send_command (imap->io, imap->tag_str, NULL,
                                       "LOGIN", user, pass, NULL);
       _mu_imap_xscript_level (imap, MU_XSCRIPT_NORMAL);
       /* FIXME: how to obscure the passwd in the stream buffer? */
diff --git a/libproto/imap/mbcreate.c b/libproto/imap/mbcreate.c
index a9b7edd..a0a3f95 100644
--- a/libproto/imap/mbcreate.c
+++ b/libproto/imap/mbcreate.c
@@ -39,7 +39,8 @@ mu_imap_mailbox_create (mu_imap_t imap, const char *mailbox)
   com.rx_state = MU_IMAP_CLIENT_DELETE_RX;
   com.argc = 2;
   com.argv = argv;
-  com.extra = 0;
+  com.extra = NULL;
+  com.msgset = NULL;
   com.tagged_handler = NULL;
   com.untagged_handler = NULL;
 
diff --git a/libproto/imap/mbox.c b/libproto/imap/mbox.c
index 609c86c..1613ba3 100644
--- a/libproto/imap/mbox.c
+++ b/libproto/imap/mbox.c
@@ -38,6 +38,7 @@
 #include <mailutils/attribute.h>
 #include <mailutils/header.h>
 #include <mailutils/body.h>
+#include <mailutils/msgset.h>
 #include <mailutils/sys/imap.h>
 
 #define _imap_mbx_clrerr(imbx) ((imbx)->last_error = 0)
@@ -58,7 +59,7 @@ _imap_msg_no (struct _mu_imap_message *imsg)
 }
 
 static int
-_imap_fetch_with_callback (mu_imap_t imap, char *msgset, char *items,
+_imap_fetch_with_callback (mu_imap_t imap, mu_msgset_t msgset, char *items,
                           mu_imap_callback_t cb, void *data)
 {
   int rc;
@@ -156,7 +157,7 @@ __imap_msg_get_stream (struct _mu_imap_message *imsg, 
size_t msgno,
   
   if (!(imsg->flags & _MU_IMAP_MSG_CACHED))
     {
-      char *msgset;
+      mu_msgset_t msgset;
       
       mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1,
                (_("caching message %lu"), (unsigned long) msgno));
@@ -174,16 +175,22 @@ __imap_msg_get_stream (struct _mu_imap_message *imsg, 
size_t msgno,
       if (rc)
        return rc;
 
-      rc = mu_asprintf (&msgset, "%lu", msgno);
+      rc = mu_msgset_create (&msgset, NULL, 0);
       if (rc == 0)
        {
          struct save_closure clos;
+
          clos.imsg = imsg;
          clos.save_stream = imbx->cache;
-         _imap_mbx_clrerr (imbx);
-         rc = _imap_fetch_with_callback (imap, msgset, "BODY[]",
-                                         _save_message, &clos);
-         free (msgset);
+
+         rc = mu_msgset_add_range (msgset, msgno, msgno);
+         if (rc == 0)
+           {
+             _imap_mbx_clrerr (imbx);
+             rc = _imap_fetch_with_callback (imap, msgset, "BODY[]",
+                                             _save_message, &clos);
+           }
+         mu_msgset_free (msgset);
          if (rc == 0 && !_imap_mbx_errno (imbx))
            {
              mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1,
@@ -359,46 +366,54 @@ _imap_hdr_fill (void *data, char **pbuf, size_t *plen)
   struct _mu_imap_mailbox *imbx = imsg->imbx;
   mu_imap_t imap = imbx->mbox->folder->data;
   struct save_closure clos;
-  char *msgset;
+  mu_msgset_t msgset;
   unsigned long msgno = _imap_msg_no (imsg);
   int rc;
-  
-  clos.imsg = imsg;
-  rc = mu_memory_stream_create (&clos.save_stream, MU_STREAM_RDWR);
-  _imap_mbx_clrerr (imbx);
-  rc = mu_asprintf (&msgset, "%lu", msgno);
+
+  rc = mu_msgset_create (&msgset, NULL, 0);
   if (rc == 0)
     {
-      mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1,
-               (_("message %lu: reading headers"),
-                (unsigned long) msgno));
-      rc = _imap_fetch_with_callback (imap, msgset, "BODY.PEEK[HEADER]",
-                                     _save_message, &clos);
-      free (msgset);
+      rc = mu_msgset_add_range (msgset, msgno, msgno);
       if (rc == 0)
        {
-         char *buf;
-         mu_off_t size;
-         
-         mu_stream_size (clos.save_stream, &size);
-         buf = malloc (size + 1);
-         if (!buf)
-           rc = ENOMEM;
-         else
+         clos.imsg = imsg;
+         rc = mu_memory_stream_create (&clos.save_stream, MU_STREAM_RDWR);
+         if (rc == 0)
            {
-             mu_stream_seek (clos.save_stream, 0, MU_SEEK_SET, NULL);
-             rc = mu_stream_read (clos.save_stream, buf, size, NULL);
+             mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1,
+                       (_("message %lu: reading headers"),
+                        (unsigned long) msgno));
+             _imap_mbx_clrerr (imbx);
+             rc = _imap_fetch_with_callback (imap, msgset,
+                                             "BODY.PEEK[HEADER]",
+                                             _save_message, &clos);
              if (rc == 0)
                {
-                 *pbuf = buf;
-                 *plen = size;
+                 char *buf;
+                 mu_off_t size;
+         
+                 mu_stream_size (clos.save_stream, &size);
+                 buf = malloc (size + 1);
+                 if (!buf)
+                   rc = ENOMEM;
+                 else
+                   {
+                     mu_stream_seek (clos.save_stream, 0, MU_SEEK_SET, NULL);
+                     rc = mu_stream_read (clos.save_stream, buf, size, NULL);
+                     if (rc == 0)
+                       {
+                         *pbuf = buf;
+                         *plen = size;
+                       }
+                     else
+                       free (buf);
+                   }
                }
-             else
-               free (buf);
+             mu_stream_destroy (&clos.save_stream);
            }
        }
+      mu_msgset_free (msgset);
     }
-  mu_stream_destroy (&clos.save_stream);
   return rc;
 }
 
@@ -602,15 +617,18 @@ _imap_msg_bodystructure (mu_message_t msg, struct 
mu_bodystructure **pbs)
   struct _mu_imap_mailbox *imbx = imsg->imbx;
   mu_imap_t imap = imbx->mbox->folder->data;
   int rc;
-  char *msgset;
-  
-  rc = mu_asprintf (&msgset, "%lu", _imap_msg_no (imsg));
-  if (rc)
-    return rc;
+  mu_msgset_t msgset;
 
-  rc = _imap_fetch_with_callback (imap, msgset, "BODYSTRUCTURE",
-                                 _imap_bodystructure_callback, pbs);
-  free (msgset);
+  rc = mu_msgset_create (&msgset, NULL, 0);
+  if (rc == 0)
+    {
+      size_t msgno = _imap_msg_no (imsg);
+      rc = mu_msgset_add_range (msgset, msgno, msgno);
+      if (rc == 0)
+       rc = _imap_fetch_with_callback (imap, msgset, "BODYSTRUCTURE",
+                                       _imap_bodystructure_callback, pbs);
+      mu_msgset_free (msgset);
+    }
   return rc;
 }
 
@@ -919,36 +937,22 @@ aggregate_attributes (struct _mu_imap_mailbox *imbx,
 }
 
 static int
-flush_attributes (mu_imap_t imap, mu_stream_t str, int attr_flags)
-{
-  int rc;
-  mu_transport_t trans[2];
-                     
-  mu_stream_write (str, "", 1, NULL);
-  if (mu_stream_err (str))
-    return mu_stream_last_error (str);
-
-  rc = mu_stream_ioctl (str, MU_IOCTL_TRANSPORT, MU_IOCTL_OP_GET, trans);
-  if (rc == 0)
-    rc = mu_imap_store_flags (imap, 0, (char*)trans[0],
-                             MU_IMAP_STORE_SET|MU_IMAP_STORE_SILENT,
-                             attr_flags);
-  return rc;
-}
-
-static int
 _imap_mbx_gensync (mu_mailbox_t mbox, int *pdel)
 {
   struct _mu_imap_mailbox *imbx = mbox->data;
   mu_folder_t folder = mbox->folder;
   mu_imap_t imap = folder->data;
   size_t i, j;
-  char *msgset;
+  mu_msgset_t msgset;
   int rc;
   int delflg = 0;
   struct attr_tab *tab;
   size_t count;
-  
+
+  rc = mu_msgset_create (&msgset, NULL, 0);
+  if (rc)
+    return rc;
+
   rc = aggregate_attributes (imbx, &tab, &count);
   if (rc)
     {
@@ -957,14 +961,14 @@ _imap_mbx_gensync (mu_mailbox_t mbox, int *pdel)
        {
          if (imbx->msgs[i].flags & _MU_IMAP_MSG_ATTRCHG)
            {
-             rc = mu_asprintf (&msgset, "%lu", i + 1);
+             mu_msgset_clear (msgset);
+             mu_msgset_add_range (msgset, i + 1, i + 1);
              if (rc)
                break;
              rc = mu_imap_store_flags (imap, 0, msgset,
                                        MU_IMAP_STORE_SET|MU_IMAP_STORE_SILENT,
                                        imbx->msgs[i].attr_flags);
              delflg |= imbx->msgs[i].attr_flags & MU_ATTRIBUTE_DELETED;
-             free (msgset);
              if (rc)
                break;
            }
@@ -972,45 +976,45 @@ _imap_mbx_gensync (mu_mailbox_t mbox, int *pdel)
     }
   else
     {
-      mu_stream_t str;
-
-      rc = mu_memory_stream_create (&str, MU_STREAM_RDWR);
-      if (rc == 0)
+      for (i = j = 0; i < count; i++)
        {
-         for (i = j = 0; i < count; i++)
+         if (j < i)
            {
-             if (j < i)
+             if (tab[j].attr_flags != tab[i].attr_flags)
                {
-                 if (tab[j].attr_flags != tab[i].attr_flags)
-                   {
-                     rc = flush_attributes (imap, str, tab[j].attr_flags);
-                     delflg |= tab[j].attr_flags & MU_ATTRIBUTE_DELETED;
-                     if (rc)
-                       break;
-                     mu_stream_truncate (str, 0);
-                     j = i;
-                   }
-                 else
-                   mu_stream_printf (str, ",");
+                 rc = mu_imap_store_flags (imap, 0, msgset,
+                                           MU_IMAP_STORE_SET|
+                                           MU_IMAP_STORE_SILENT,
+                                           tab[j].attr_flags);
+                 delflg |= tab[j].attr_flags & MU_ATTRIBUTE_DELETED;
+                 if (rc)
+                   break;
+                 mu_msgset_clear (msgset);
+                 j = i;
                }
-             if (tab[i].end == tab[i].start)
-               mu_stream_printf (str, "%lu", (unsigned long)tab[i].start+1);
-             else
-               mu_stream_printf (str, "%lu:%lu",
-                                 (unsigned long)tab[i].start+1,
-                                 (unsigned long)tab[i].end+1);
            }
+         if (tab[i].end == tab[i].start)
+           rc = mu_msgset_add_range (msgset, tab[i].start + 1,
+                                     tab[i].start + 1);
+         else
+           rc = mu_msgset_add_range (msgset,
+                                     tab[i].start + 1,
+                                     tab[i].end + 1);
+         if (rc)
+           break;
+       }
 
-         if (j < i)
-           {
-             rc = flush_attributes (imap, str, tab[j].attr_flags);
-             delflg |= tab[j].attr_flags & MU_ATTRIBUTE_DELETED;
-           }
-         mu_stream_unref (str);
+      if (rc == 0 && j < i)
+       {
+         rc = mu_imap_store_flags (imap, 0, msgset,
+                                   MU_IMAP_STORE_SET|MU_IMAP_STORE_SILENT,
+                                   tab[j].attr_flags);
+         delflg |= tab[j].attr_flags & MU_ATTRIBUTE_DELETED;
        }
       free (tab);
     }
-
+  mu_msgset_free (msgset);
+  
   if (rc)
     return rc;
 
@@ -1182,20 +1186,26 @@ _imap_mbx_scan (mu_mailbox_t mbox, size_t msgno, size_t 
*pcount)
   struct _mu_imap_mailbox *imbx = mbox->data;
   mu_folder_t folder = mbox->folder;
   mu_imap_t imap = folder->data;
-  char *msgset;
+  mu_msgset_t msgset;
   int rc;
   static char _imap_scan_items[] = "(UID FLAGS ENVELOPE RFC822.SIZE BODY)";
   
   mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1,
            (_("scanning mailbox %s"), mu_url_to_string (mbox->url)));
-  rc = mu_asprintf (&msgset, "%lu:*", (unsigned long) msgno);
+  rc = mu_msgset_create (&msgset, NULL, 0);
   if (rc)
     return rc;
-
+  rc = mu_msgset_add_range (msgset, msgno, MU_MSGNO_LAST);
+  if (rc)
+    {
+      mu_msgset_free (msgset);
+      return rc;
+    }
+  
   _imap_mbx_clrerr (imbx);
   rc = _imap_fetch_with_callback (imap, msgset, _imap_scan_items,
                                  _imap_fetch_callback, imbx);
-  free (msgset);
+  mu_msgset_free (msgset);
   if (rc == 0)
     rc = _imap_mbx_errno (imbx);
   if (rc == 0)
diff --git a/libproto/imap/rename.c b/libproto/imap/rename.c
index 3348380..dc5f0d5 100644
--- a/libproto/imap/rename.c
+++ b/libproto/imap/rename.c
@@ -42,6 +42,7 @@ mu_imap_rename (mu_imap_t imap, const char *mailbox, const 
char *new_mailbox)
   com.argc = 3;
   com.argv = argv;
   com.extra = NULL;
+  com.msgset = NULL;
   com.tagged_handler = NULL;
   com.untagged_handler = NULL;
 
diff --git a/libproto/imap/select.c b/libproto/imap/select.c
index 31c2b72..bd78218 100644
--- a/libproto/imap/select.c
+++ b/libproto/imap/select.c
@@ -102,7 +102,7 @@ mu_imap_select (mu_imap_t imap, const char *mbox, int 
writable,
     case MU_IMAP_CLIENT_READY:
       status = _mu_imap_tag_next (imap);
       MU_IMAP_CHECK_EAGAIN (imap, status);
-      status = mu_imapio_send_command (imap->io, imap->tag_str,
+      status = mu_imapio_send_command (imap->io, imap->tag_str, NULL,
                                       writable ? "SELECT" : "EXAMINE",
                                       mbox, NULL);
       MU_IMAP_CHECK_ERROR (imap, status);
diff --git a/libproto/imap/store.c b/libproto/imap/store.c
index be1349f..acec7a1 100644
--- a/libproto/imap/store.c
+++ b/libproto/imap/store.c
@@ -26,7 +26,7 @@
 #include <mailutils/sys/imap.h>
 
 int
-mu_imap_store (mu_imap_t imap, int uid, const char *msgset, const char *items)
+mu_imap_store (mu_imap_t imap, int uid, mu_msgset_t msgset, const char *items)
 {
   char const *argv[3];
   int i;
@@ -36,7 +36,7 @@ mu_imap_store (mu_imap_t imap, int uid, const char *msgset, 
const char *items)
   if (uid)
     argv[i++] = "UID";
   argv[i++] = "STORE";
-  argv[i++] = msgset;
+  argv[i++] = "\\";
   
   com.session_state = MU_IMAP_SESSION_SELECTED;
   com.capa = NULL;
@@ -44,6 +44,7 @@ mu_imap_store (mu_imap_t imap, int uid, const char *msgset, 
const char *items)
   com.argc = i;
   com.argv = argv;
   com.extra = items;
+  com.msgset = msgset;
   com.tagged_handler = NULL;
   com.untagged_handler = NULL;
 
diff --git a/libproto/imap/storeflg.c b/libproto/imap/storeflg.c
index eba59e4..8bc2e90 100644
--- a/libproto/imap/storeflg.c
+++ b/libproto/imap/storeflg.c
@@ -29,7 +29,7 @@
 #include <mailutils/sys/imap.h>
 
 int
-mu_imap_store_flags (mu_imap_t imap, int uid, const char *msgset,
+mu_imap_store_flags (mu_imap_t imap, int uid, mu_msgset_t msgset,
                     int op, int flags)
 {
   int status;
@@ -50,8 +50,9 @@ mu_imap_store_flags (mu_imap_t imap, int uid, const char 
*msgset,
       mu_imapio_printf (imap->io, "%s ", imap->tag_str);
       if (uid)
        mu_imapio_printf (imap->io, "UID ");
-      mu_imapio_printf (imap->io, "STORE %s %s", msgset,
-                       cmd[op & MU_IMAP_STORE_OPMASK]);
+      mu_imapio_printf (imap->io, "STORE ");
+      mu_imapio_send_msgset (imap->io, msgset);
+      mu_imapio_printf (imap->io, " %s", cmd[op & MU_IMAP_STORE_OPMASK]);
       if (op & MU_IMAP_STORE_SILENT)
        mu_imapio_printf (imap->io, ".SILENT");
       mu_imapio_printf (imap->io, " ");
diff --git a/libproto/imap/subscribe.c b/libproto/imap/subscribe.c
index f370f3a..3a71584 100644
--- a/libproto/imap/subscribe.c
+++ b/libproto/imap/subscribe.c
@@ -41,6 +41,7 @@ mu_imap_subscribe (mu_imap_t imap, const char *mailbox)
   com.argc = 2;
   com.argv = argv;
   com.extra = NULL;
+  com.msgset = NULL;
   com.tagged_handler = NULL;
   com.untagged_handler = NULL;
 
diff --git a/libproto/imap/unselect.c b/libproto/imap/unselect.c
index c1ac499..3c23241 100644
--- a/libproto/imap/unselect.c
+++ b/libproto/imap/unselect.c
@@ -33,6 +33,7 @@ mu_imap_unselect (mu_imap_t imap)
     1,
     &command,
     NULL,
+    NULL,
     _mu_close_handler
   };
   return mu_imap_gencom (imap, &com);
diff --git a/libproto/imap/unsubscribe.c b/libproto/imap/unsubscribe.c
index 159b442..fc1084f 100644
--- a/libproto/imap/unsubscribe.c
+++ b/libproto/imap/unsubscribe.c
@@ -41,6 +41,7 @@ mu_imap_unsubscribe (mu_imap_t imap, const char *mailbox)
   com.argc = 2;
   com.argv = argv;
   com.extra = NULL;
+  com.msgset = NULL;
   com.tagged_handler = NULL;
   com.untagged_handler = NULL;
 
diff --git a/mu/imap.c b/mu/imap.c
index 0069ff5..8e3e2bf 100644
--- a/mu/imap.c
+++ b/mu/imap.c
@@ -27,6 +27,7 @@
 #include <mailutils/imap.h>
 #include <mailutils/imapio.h>
 #include <mailutils/imaputil.h>
+#include <mailutils/msgset.h>
 #include "mu.h"
 #include "argp.h"
 #include "xalloc.h"
@@ -110,6 +111,29 @@ com_verbose (int argc, char **argv)
   
 }
 
+static mu_msgset_t
+parse_msgset (const char *arg)
+{
+  int status;
+  mu_msgset_t msgset;
+  char *p;
+  
+  status = mu_msgset_create (&msgset, NULL, 0);
+  if (status)
+    {
+      mu_diag_funcall (MU_DIAG_ERROR, "mu_msgset_create", NULL, status);
+      return NULL;
+    }
+  status = mu_msgset_parse_imap (msgset, arg, &p);
+  if (status)
+    {
+      mu_error (_("failed to parse message set near \"%s\": %s"),
+               p, mu_strerror (status));
+      mu_msgset_destroy (&msgset);
+    }
+  return msgset;
+}
+
 static void
 report_failure (const char *what, int status)
 {
@@ -898,28 +922,39 @@ com_check (int argc MU_ARG_UNUSED, char **argv 
MU_ARG_UNUSED)
 static int
 com_fetch (int argc, char **argv)
 {
-  int status;
   mu_stream_t out = mutool_open_pager ();
-  
-  mu_imap_register_callback_function (imap, MU_IMAP_CB_FETCH,
-                                     imap_fetch_callback,
-                                     out);
-  status = mu_imap_fetch (imap, uid_mode, argv[1], argv[2]);
-  mu_stream_destroy (&out);
-  mu_imap_register_callback_function (imap, MU_IMAP_CB_FETCH,
-                                     imap_fetch_callback,
-                                     mu_strout);
-  if (status)
-    report_failure ("fetch", status);
+  mu_msgset_t msgset = parse_msgset (argv[1]);
+
+  if (msgset)
+    {
+      int status;
+
+      mu_imap_register_callback_function (imap, MU_IMAP_CB_FETCH,
+                                         imap_fetch_callback,
+                                         out);
+      status = mu_imap_fetch (imap, uid_mode, msgset, argv[2]);
+      mu_stream_destroy (&out);
+      mu_imap_register_callback_function (imap, MU_IMAP_CB_FETCH,
+                                         imap_fetch_callback,
+                                         mu_strout);
+      mu_msgset_free (msgset);
+      if (status)
+       report_failure ("fetch", status);
+    }
   return 0;  
 }
 
 static int
 com_store (int argc, char **argv)
 {
-  int status = mu_imap_store (imap, uid_mode, argv[1], argv[2]);
-  if (status)
-    report_failure ("store", status);
+  mu_msgset_t msgset = parse_msgset (argv[1]);
+
+  if (msgset)
+    {
+      int status = mu_imap_store (imap, uid_mode, msgset, argv[2]);
+      if (status)
+       report_failure ("store", status);
+    }
   return 0;
 }
 
@@ -1076,9 +1111,14 @@ com_append (int argc, char **argv)
 static int
 com_copy (int argc, char **argv)
 {
-  int status = mu_imap_copy (imap, uid_mode, argv[1], argv[2]);
-  if (status)
-    report_failure ("copy", status);
+  mu_msgset_t msgset = parse_msgset (argv[1]);
+  if (msgset)
+    {
+      int status = mu_imap_copy (imap, uid_mode, msgset, argv[2]);
+      mu_msgset_free (msgset);
+      if (status)
+       report_failure ("copy", status);
+    }
   return 0;
 }
 
diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am
index 2a9f4ea..f115a86 100644
--- a/testsuite/Makefile.am
+++ b/testsuite/Makefile.am
@@ -54,6 +54,7 @@ noinst_PROGRAMS = \
  fldel\
  lstuid\
  mbdel\
+ msgset\
  mimetest\
  smtpsend\
  ufms
diff --git a/testsuite/msgset.c b/testsuite/msgset.c
new file mode 100644
index 0000000..ae2a099
--- /dev/null
+++ b/testsuite/msgset.c
@@ -0,0 +1,135 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+   Copyright (C) 2011 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/>. */
+
+#include <config.h>
+#include <stdlib.h>
+#include <mailutils/mailutils.h>
+
+static void
+parse_msgset (char *arg, struct mu_msgrange *range)
+{
+  size_t msgnum;
+  char *p;
+  
+  errno = 0;
+  msgnum = strtoul (arg, &p, 10);
+  range->msg_beg = msgnum;
+  if (*p == ':')
+    {
+      if (*++p == '*')
+       msgnum = 0;
+      else
+       {
+         msgnum = strtoul (p, &p, 10);
+         if (*p)
+           {
+             mu_error ("error in message range near %s", p);
+             exit (1);
+           }
+       }
+    }
+  else if (*p == '*')
+    msgnum = 0;
+  else if (*p)
+    {
+      mu_error ("error in message range near %s", p);
+      exit (1);
+    }
+
+  range->msg_end = msgnum;
+}
+
+int
+main (int argc, char **argv)
+{
+  int i;
+  char *msgset_string = NULL;
+  mu_msgset_t msgset;
+  int rc;
+  int flags = 0;
+  mu_mailbox_t mbox = NULL;
+  
+  mu_set_program_name (argv[0]);
+  mu_registrar_record (mu_mbox_record);
+  for (i = 1; i < argc; i++)
+    {
+      char *arg = argv[i];
+
+      if (strcmp (arg, "-h") == 0 || strcmp (arg, "-help") == 0)
+       {
+         mu_printf ("usage: %s [-msgset=SET] [-add X[:Y]] [-del X:[Y]]...\n",
+                    mu_program_name);
+         return 0;
+       }
+      else if (strncmp (arg, "-msgset=", 8) == 0)
+       msgset_string = arg + 8;
+      else if (strcmp (arg, "-uid") == 0)
+       flags |= MU_MSGSET_UID;
+      else if (strncmp (arg, "-mailbox=", 9) == 0)
+       {
+         MU_ASSERT (mu_mailbox_create (&mbox, arg + 9));
+         MU_ASSERT (mu_mailbox_open (mbox, MU_STREAM_READ));
+       }
+      else
+       break;
+    }
+
+  MU_ASSERT (mu_msgset_create (&msgset, mbox, flags));
+  if (msgset_string)
+    {
+      char *end;
+      rc = mu_msgset_parse_imap (msgset, msgset_string, &end);
+      if (rc)
+       {
+         mu_error ("mu_msgset_parse_imap: %s near %s",
+                   mu_strerror (rc), end);
+         return 1;
+       }
+    }
+  
+  for (; i < argc; i++)
+    {
+      char *arg = argv[i];
+      struct mu_msgrange range;
+      
+      if (strncmp (arg, "-add=", 5) == 0)
+       {
+         parse_msgset (arg + 5, &range);
+         MU_ASSERT (mu_msgset_add_range (msgset, range.msg_beg,
+                                         range.msg_end));
+       }
+      else if (strncmp (arg, "-sub=", 5) == 0)
+       {
+         parse_msgset (arg + 5, &range);
+         MU_ASSERT (mu_msgset_sub_range (msgset, range.msg_beg,
+                                         range.msg_end));
+       }
+      else
+       {
+         mu_error ("unknown option %s", arg);
+         return 1;
+       }
+    }
+  MU_ASSERT (mu_msgset_print (mu_strout, msgset));
+  mu_printf ("\n");
+  mu_msgset_free (msgset);
+  
+  return 0;
+}
+  
+            
+
+       


hooks/post-receive
-- 
GNU Mailutils



reply via email to

[Prev in Thread] Current Thread [Next in Thread]