Commit cef5f35d cef5f35da5f0e9b326d4d9a56f6b62ad565ad643 by Sergey Poznyakoff

Redo message set parser in imap4d

The new parser complies to RFC3501. Its output message sets are
formatted as MU lists of non-overlapping contiguous message ranges,
which reduces memory consumption and imposes less strain on CPU.
The parser automatically translates message UIDs to sequence numbers
and provides better error handling.

* imap4d/imap4d.h (util_msgset): Remove.
(util_parse_msgset): New proto.
(imap4d_message_action_t): New typedef.
(util_foreach_message): New proto.
* imap4d/util.c  (util_msgset): Remove.
(util_parse_msgset): New function.

* imap4d/copy.c: Use util_parse_msgset to parse message set specs and
util_foreach_message to iterate over the returned list.
* imap4d/fetch.c: Likewise.
* imap4d/search.c: Likewise.
* imap4d/store.c: Likewise.
* imap4d/tests/IDEF0955.at: Update the test.
* include/mailutils/list.h (mu_list_action_t): Fix typedef.
* libmailutils/list/foreach.c (mu_list_foreach)
(mu_list_do): Update signatures.
1 parent f9a034c7
...@@ -55,88 +55,111 @@ imap4d_copy (struct imap4d_command *command, imap4d_tokbuf_t tok) ...@@ -55,88 +55,111 @@ imap4d_copy (struct imap4d_command *command, imap4d_tokbuf_t tok)
55 return io_completion_response (command, rc, "%s", text); 55 return io_completion_response (command, rc, "%s", text);
56 } 56 }
57 57
58 static int 58 struct copy_env
59 copy_check_size (mu_mailbox_t mbox, size_t n, size_t *set, mu_off_t *size)
60 { 59 {
61 int status; 60 mu_mailbox_t dst;
62 size_t i; 61 mu_mailbox_t src;
63 mu_off_t total = 0; 62 mu_off_t total;
63 int ret;
64 char **err_text;
65 };
64 66
65 for (i = 0; i < n; i++) 67 static int
66 { 68 size_sum (size_t msgno, void *data)
69 {
70 struct copy_env *env = data;
67 mu_message_t msg = NULL; 71 mu_message_t msg = NULL;
68 size_t msgno = set[i]; 72 int rc;
69 if (msgno) 73
70 { 74 rc = mu_mailbox_get_message (env->src, msgno, &msg);
71 status = mu_mailbox_get_message (mbox, msgno, &msg); 75 if (rc)
72 if (status)
73 { 76 {
74 mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_get_message", NULL, 77 mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_get_message", NULL, rc);
75 status); 78 env->ret = RESP_NO;
76 return RESP_BAD; 79 return MU_ERR_FAILURE;
77 } 80 }
78 else 81 else
79 { 82 {
80 size_t size; 83 size_t size;
81 status = mu_message_size (msg, &size); 84 rc = mu_message_size (msg, &size);
82 if (status) 85 if (rc)
83 { 86 {
84 mu_diag_funcall (MU_DIAG_ERROR, "mu_message_size", NULL, 87 mu_diag_funcall (MU_DIAG_ERROR, "mu_message_size", NULL, rc);
85 status); 88 env->ret = RESP_BAD;
86 return RESP_BAD; 89 return MU_ERR_FAILURE;
87 }
88 total += size;
89 }
90 } 90 }
91 env->total += size;
91 } 92 }
92 *size = total; 93 return 0;
93 return quota_check (total);
94 } 94 }
95 95
96 static int 96 static int
97 try_copy (mu_mailbox_t dst, mu_mailbox_t src, size_t n, size_t *set) 97 do_copy (size_t msgno, void *data)
98 { 98 {
99 int result; 99 struct copy_env *env = data;
100 size_t i;
101 mu_off_t total;
102
103 result = copy_check_size (src, n, set, &total);
104 if (result)
105 return result;
106
107 for (i = 0; i < n; i++)
108 {
109 mu_message_t msg = NULL; 100 mu_message_t msg = NULL;
110 size_t msgno = set[i]; 101 int status;
111 102
112 if (msgno) 103 status = mu_mailbox_get_message (env->src, msgno, &msg);
113 {
114 int status = mu_mailbox_get_message (src, msgno, &msg);
115 if (status) 104 if (status)
116 { 105 {
117 mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_get_message", NULL, 106 mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_get_message", NULL,
118 status); 107 status);
119 return RESP_BAD; 108 env->ret = RESP_BAD;
109 return MU_ERR_FAILURE;
120 } 110 }
121 111
122 imap4d_enter_critical (); 112 imap4d_enter_critical ();
123 status = mu_mailbox_append_message (dst, msg); 113 status = mu_mailbox_append_message (env->dst, msg);
124 imap4d_leave_critical (); 114 imap4d_leave_critical ();
125 if (status) 115 if (status)
126 { 116 {
127 mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_append_message", 117 mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_append_message", NULL,
128 NULL,
129 status); 118 status);
130 return RESP_BAD; 119 env->ret = RESP_BAD;
131 } 120 return MU_ERR_FAILURE;
132 } 121 }
122
123 return 0;
124 }
125
126 static int
127 try_copy (mu_mailbox_t dst, mu_mailbox_t src, mu_list_t msglist,
128 char **err_text)
129 {
130 int rc;
131 struct copy_env env;
132
133 env.dst = dst;
134 env.src = src;
135 env.total = 0;
136 env.ret = RESP_OK;
137 env.err_text = err_text;
138
139 *env.err_text = "Operation failed";
140
141 /* Check size */
142 rc = util_foreach_message (msglist, size_sum, &env);
143 if (rc)
144 return RESP_NO;
145 if (env.ret != RESP_OK)
146 return env.ret;
147 rc = quota_check (env.total);
148 if (rc)
149 {
150 *env.err_text = "Mailbox quota exceeded";
151 return RESP_NO;
133 } 152 }
134 quota_update (total); 153 env.total = 0;
135 return RESP_OK; 154 rc = util_foreach_message (msglist, do_copy, &env);
155 quota_update (env.total);
156 if (rc)
157 return RESP_NO;
158 return env.ret;
136 } 159 }
137 160
138 static int 161 static int
139 safe_copy (mu_mailbox_t dst, mu_mailbox_t src, size_t n, size_t *set, 162 safe_copy (mu_mailbox_t dst, mu_mailbox_t src, mu_list_t msglist,
140 char **err_text) 163 char **err_text)
141 { 164 {
142 size_t nmesg; 165 size_t nmesg;
...@@ -151,16 +174,11 @@ safe_copy (mu_mailbox_t dst, mu_mailbox_t src, size_t n, size_t *set, ...@@ -151,16 +174,11 @@ safe_copy (mu_mailbox_t dst, mu_mailbox_t src, size_t n, size_t *set,
151 return RESP_NO; 174 return RESP_NO;
152 } 175 }
153 176
154 status = try_copy (dst, src, n, set); 177 status = try_copy (dst, src, msglist, err_text);
155 if (status) 178 if (status != RESP_OK)
156 { 179 {
157 size_t maxmesg; 180 size_t maxmesg;
158 181
159 if (status == RESP_NO)
160 *err_text = "Mailbox quota exceeded";
161 else
162 *err_text = "Operation failed";
163
164 /* If the COPY command is unsuccessful for any reason, server 182 /* If the COPY command is unsuccessful for any reason, server
165 implementations MUST restore the destination mailbox to its state 183 implementations MUST restore the destination mailbox to its state
166 before the COPY attempt. */ 184 before the COPY attempt. */
...@@ -211,11 +229,11 @@ imap4d_copy0 (imap4d_tokbuf_t tok, int isuid, char **err_text) ...@@ -211,11 +229,11 @@ imap4d_copy0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
211 { 229 {
212 int status; 230 int status;
213 char *msgset; 231 char *msgset;
232 mu_list_t msglist;
214 char *name; 233 char *name;
215 char *mailbox_name; 234 char *mailbox_name;
216 const char *delim = "/"; 235 const char *delim = "/";
217 size_t *set = NULL; 236 char *end;
218 int n = 0;
219 mu_mailbox_t cmbox = NULL; 237 mu_mailbox_t cmbox = NULL;
220 int arg = IMAP4_ARG_1 + !!isuid; 238 int arg = IMAP4_ARG_1 + !!isuid;
221 int ns; 239 int ns;
...@@ -230,21 +248,12 @@ imap4d_copy0 (imap4d_tokbuf_t tok, int isuid, char **err_text) ...@@ -230,21 +248,12 @@ imap4d_copy0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
230 msgset = imap4d_tokbuf_getarg (tok, arg); 248 msgset = imap4d_tokbuf_getarg (tok, arg);
231 name = imap4d_tokbuf_getarg (tok, arg + 1); 249 name = imap4d_tokbuf_getarg (tok, arg + 1);
232 /* Get the message numbers in set[]. */ 250 /* Get the message numbers in set[]. */
233 status = util_msgset (msgset, &set, &n, isuid); 251 status = util_parse_msgset (msgset, isuid, mbox, &msglist, &end);
234 if (status != 0) 252 if (status)
235 {
236 /* See RFC 3501, section 6.4.8, and a comment to the equivalent code
237 in fetch.c */
238 *err_text = "Completed";
239 return RESP_OK;
240 }
241
242 if (isuid)
243 { 253 {
244 int i; 254 *err_text = "Error parsing message set";
245 /* Fixup the message set. Perhaps util_msgset should do it itself? */ 255 /* FIXME: print error location */
246 for (i = 0; i < n; i++) 256 return RESP_BAD;
247 set[i] = uid_to_msgno (set[i]);
248 } 257 }
249 258
250 mailbox_name = namespace_getfullpath (name, delim, &ns); 259 mailbox_name = namespace_getfullpath (name, delim, &ns);
...@@ -264,13 +273,13 @@ imap4d_copy0 (imap4d_tokbuf_t tok, int isuid, char **err_text) ...@@ -264,13 +273,13 @@ imap4d_copy0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
264 status = mu_mailbox_open (cmbox, MU_STREAM_RDWR | mailbox_mode[ns]); 273 status = mu_mailbox_open (cmbox, MU_STREAM_RDWR | mailbox_mode[ns]);
265 if (status == 0) 274 if (status == 0)
266 { 275 {
267 status = safe_copy (cmbox, mbox, n, set, err_text); 276 if (!mu_list_is_empty (msglist))
277 status = safe_copy (cmbox, mbox, msglist, err_text);
268 mu_mailbox_close (cmbox); 278 mu_mailbox_close (cmbox);
269 } 279 }
270 mu_mailbox_destroy (&cmbox); 280 mu_mailbox_destroy (&cmbox);
271 } 281 }
272 free (set); 282 mu_list_destroy (&msglist);
273 free (mailbox_name);
274 283
275 if (status == 0) 284 if (status == 0)
276 { 285 {
......
...@@ -37,6 +37,8 @@ struct fetch_runtime_closure ...@@ -37,6 +37,8 @@ struct fetch_runtime_closure
37 mu_message_t msg; /* The message itself */ 37 mu_message_t msg; /* The message itself */
38 mu_list_t msglist; /* A list of referenced messages. See KLUDGE below. */ 38 mu_list_t msglist; /* A list of referenced messages. See KLUDGE below. */
39 char *err_text; /* On return: error description if failed. */ 39 char *err_text; /* On return: error description if failed. */
40
41 mu_list_t fnlist;
40 }; 42 };
41 43
42 struct fetch_function_closure; 44 struct fetch_function_closure;
...@@ -62,8 +64,7 @@ struct fetch_parse_closure ...@@ -62,8 +64,7 @@ struct fetch_parse_closure
62 { 64 {
63 int isuid; 65 int isuid;
64 mu_list_t fnlist; 66 mu_list_t fnlist;
65 size_t *set; 67 mu_list_t msgnumlist;
66 int count;
67 }; 68 };
68 69
69 70
...@@ -1751,38 +1752,16 @@ fetch_thunk (imap4d_parsebuf_t pb) ...@@ -1751,38 +1752,16 @@ fetch_thunk (imap4d_parsebuf_t pb)
1751 { 1752 {
1752 int status; 1753 int status;
1753 char *msgset; 1754 char *msgset;
1755 char *end;
1754 struct fetch_parse_closure *pclos = imap4d_parsebuf_data (pb); 1756 struct fetch_parse_closure *pclos = imap4d_parsebuf_data (pb);
1755 1757
1756 msgset = imap4d_parsebuf_next (pb, 1); 1758 msgset = imap4d_parsebuf_next (pb, 1);
1757 1759
1758 /* Get the message numbers in set[]. */ 1760 /* Parse sequence numbers. */
1759 status = util_msgset (msgset, &pclos->set, &pclos->count, pclos->isuid); 1761 status = util_parse_msgset (msgset, pclos->isuid, mbox,
1760 switch (status) 1762 &pclos->msgnumlist, &end);
1761 { 1763 if (status)
1762 case 0:
1763 /* Very good! */
1764 break;
1765
1766 case EINVAL:
1767 /* RFC3501, section 6.4.8.
1768
1769 A non-existent unique identifier is ignored without any error
1770 message generated. Thus, it is possible for a UID FETCH command
1771 to return an OK without any data or a UID COPY or UID STORE to
1772 return an OK without performing any operations.
1773
1774 Obviously the same holds true for non-existing message numbers
1775 as well, although I did not find any explicit mention thereof
1776 in the RFC.
1777
1778 FIXME: This code also causes imap4d to silently ignore erroneous
1779 msgset specifications (e.g. FETCH foobar (FLAGS)), which should
1780 be fixed. */
1781 return RESP_OK;
1782
1783 default:
1784 imap4d_parsebuf_exit (pb, "Failed to parse message set"); 1764 imap4d_parsebuf_exit (pb, "Failed to parse message set");
1785 }
1786 1765
1787 /* Compile the expression */ 1766 /* Compile the expression */
1788 1767
...@@ -1797,6 +1776,23 @@ fetch_thunk (imap4d_parsebuf_t pb) ...@@ -1797,6 +1776,23 @@ fetch_thunk (imap4d_parsebuf_t pb)
1797 return RESP_OK; 1776 return RESP_OK;
1798 } 1777 }
1799 1778
1779 int
1780 _fetch_from_message (size_t msgno, void *data)
1781 {
1782 int rc = 0;
1783 struct fetch_runtime_closure *frc = data;
1784
1785 frc->msgno = msgno;
1786 if (mu_mailbox_get_message (mbox, msgno, &frc->msg) == 0)
1787 {
1788 io_sendf ("* %lu FETCH (", (unsigned long) msgno);
1789 frc->eltno = 0;
1790 rc = mu_list_foreach (frc->fnlist, _do_fetch, frc);
1791 io_sendf (")\n");
1792 }
1793 return rc;
1794 }
1795
1800 /* Where the real implementation is. It is here since UID command also 1796 /* Where the real implementation is. It is here since UID command also
1801 calls FETCH. */ 1797 calls FETCH. */
1802 int 1798 int
...@@ -1823,32 +1819,20 @@ imap4d_fetch0 (imap4d_tokbuf_t tok, int isuid, char **err_text) ...@@ -1823,32 +1819,20 @@ imap4d_fetch0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
1823 1819
1824 if (rc == RESP_OK) 1820 if (rc == RESP_OK)
1825 { 1821 {
1826 size_t i;
1827 struct fetch_runtime_closure frc; 1822 struct fetch_runtime_closure frc;
1828 1823
1829 memset (&frc, 0, sizeof (frc)); 1824 memset (&frc, 0, sizeof (frc));
1825 frc.fnlist = pclos.fnlist;
1830 /* Prepare status code. It will be replaced if an error occurs in the 1826 /* Prepare status code. It will be replaced if an error occurs in the
1831 loop below */ 1827 loop below */
1832 frc.err_text = "Completed"; 1828 frc.err_text = "Completed";
1833 1829
1834 for (i = 0; i < pclos.count && rc == RESP_OK; i++) 1830 util_foreach_message (pclos.msgnumlist, _fetch_from_message, &frc);
1835 {
1836 frc.msgno = (isuid) ? uid_to_msgno (pclos.set[i]) : pclos.set[i];
1837
1838 if (frc.msgno &&
1839 mu_mailbox_get_message (mbox, frc.msgno, &frc.msg) == 0)
1840 {
1841 io_sendf ("* %lu FETCH (", (unsigned long) frc.msgno);
1842 frc.eltno = 0;
1843 rc = mu_list_foreach (pclos.fnlist, _do_fetch, &frc);
1844 io_sendf (")\n");
1845 }
1846 }
1847 mu_list_destroy (&frc.msglist); 1831 mu_list_destroy (&frc.msglist);
1848 } 1832 }
1849 1833
1850 mu_list_destroy (&pclos.fnlist); 1834 mu_list_destroy (&pclos.fnlist);
1851 free (pclos.set); 1835 mu_list_destroy (&pclos.msgnumlist);
1852 return rc; 1836 return rc;
1853 } 1837 }
1854 1838
......
...@@ -554,7 +554,6 @@ imap4d_master_signal (int signo) ...@@ -554,7 +554,6 @@ imap4d_master_signal (int signo)
554 } 554 }
555 555
556 556
557
558 int 557 int
559 main (int argc, char **argv) 558 main (int argc, char **argv)
560 { 559 {
......
...@@ -368,10 +368,17 @@ extern int util_getstate (void); ...@@ -368,10 +368,17 @@ extern int util_getstate (void);
368 extern int util_do_command (imap4d_tokbuf_t); 368 extern int util_do_command (imap4d_tokbuf_t);
369 extern char *util_tilde_expansion (const char *, const char *); 369 extern char *util_tilde_expansion (const char *, const char *);
370 extern char *util_getfullpath (const char *, const char *); 370 extern char *util_getfullpath (const char *, const char *);
371 extern int util_msgset (char *, size_t **, int *, int);
372 extern struct imap4d_command *util_getcommand (char *, 371 extern struct imap4d_command *util_getcommand (char *,
373 struct imap4d_command []); 372 struct imap4d_command []);
374 373
374 extern int util_parse_msgset (char *s, int isuid, mu_mailbox_t mbx,
375 mu_list_t *plist, char **end);
376
377 typedef int (*imap4d_message_action_t) (size_t, void *);
378
379 int util_foreach_message (mu_list_t list, imap4d_message_action_t action,
380 void *data);
381
375 enum datetime_parse_mode /* how to parse date/time strings */ 382 enum datetime_parse_mode /* how to parse date/time strings */
376 { 383 {
377 datetime_default, /* default mode */ 384 datetime_default, /* default mode */
......
...@@ -62,11 +62,7 @@ struct value ...@@ -62,11 +62,7 @@ struct value
62 char *string; 62 char *string;
63 mu_off_t number; 63 mu_off_t number;
64 time_t date; 64 time_t date;
65 struct 65 mu_list_t msgset;
66 {
67 int n;
68 size_t *set;
69 } msgset;
70 } v; 66 } v;
71 }; 67 };
72 68
...@@ -168,7 +164,7 @@ struct cond condlist[] = ...@@ -168,7 +164,7 @@ struct cond condlist[] =
168 { "SUBJECT", "s", cond_subject }, 164 { "SUBJECT", "s", cond_subject },
169 { "TEXT", "s", cond_text }, 165 { "TEXT", "s", cond_text },
170 { "TO", "s", cond_to }, 166 { "TO", "s", cond_to },
171 { "UID", "m", cond_uid }, 167 { "UID", "u", cond_uid },
172 { NULL } 168 { NULL }
173 }; 169 };
174 170
...@@ -584,8 +580,6 @@ parse_simple_key (struct parsebuf *pb) ...@@ -584,8 +580,6 @@ parse_simple_key (struct parsebuf *pb)
584 struct search_node *node; 580 struct search_node *node;
585 struct cond *condp; 581 struct cond *condp;
586 time_t time; 582 time_t time;
587 size_t *set = NULL;
588 int n = 0;
589 583
590 for (condp = condlist; condp->name && mu_c_strcasecmp (condp->name, pb->token); 584 for (condp = condlist; condp->name && mu_c_strcasecmp (condp->name, pb->token);
591 condp++) 585 condp++)
...@@ -593,13 +587,14 @@ parse_simple_key (struct parsebuf *pb) ...@@ -593,13 +587,14 @@ parse_simple_key (struct parsebuf *pb)
593 587
594 if (!condp->name) 588 if (!condp->name)
595 { 589 {
596 if (util_msgset (pb->token, &set, &n, 0) == 0) 590 mu_list_t msglist;
591
592 if (util_parse_msgset (pb->token, pb->isuid, mbox, &msglist, NULL) == 0)
597 { 593 {
598 struct search_node *np = parse_alloc (pb, sizeof *np); 594 struct search_node *np = parse_alloc (pb, sizeof *np);
599 np->type = node_value; 595 np->type = node_value;
600 np->v.value.type = value_msgset; 596 np->v.value.type = value_msgset;
601 np->v.value.v.msgset.n = n; 597 np->v.value.v.msgset = msglist;
602 np->v.value.v.msgset.set = parse_regmem (pb, set);
603 598
604 node = parse_alloc (pb, sizeof *node); 599 node = parse_alloc (pb, sizeof *node);
605 node->type = node_call; 600 node->type = node_call;
...@@ -630,9 +625,7 @@ parse_simple_key (struct parsebuf *pb) ...@@ -630,9 +625,7 @@ parse_simple_key (struct parsebuf *pb)
630 { 625 {
631 char *t = condp->argtypes; 626 char *t = condp->argtypes;
632 char *s; 627 char *s;
633 int n;
634 mu_off_t number; 628 mu_off_t number;
635 size_t *set;
636 struct search_node *arg; 629 struct search_node *arg;
637 630
638 for (; *t; t++, parse_gettoken (pb, 0)) 631 for (; *t; t++, parse_gettoken (pb, 0))
...@@ -680,19 +673,19 @@ parse_simple_key (struct parsebuf *pb) ...@@ -680,19 +673,19 @@ parse_simple_key (struct parsebuf *pb)
680 arg->v.value.v.date = time; 673 arg->v.value.v.date = time;
681 break; 674 break;
682 675
683 case 'm': /* message set */ 676 case 'u': /* UID message set */
684 if (util_msgset (pb->token, &set, &n, 1)) /*FIXME: isuid?*/ 677 if (util_parse_msgset (pb->token, 0, NULL,
678 &arg->v.value.v.msgset, NULL))
685 { 679 {
686 pb->err_mesg = "Bogus number set"; 680 pb->err_mesg = "Bogus number set";
687 return NULL; 681 return NULL;
688 } 682 }
689 arg->v.value.type = value_msgset; 683 arg->v.value.type = value_msgset;
690 arg->v.value.v.msgset.n = n;
691 arg->v.value.v.msgset.set = parse_regmem (pb, set);
692 break; 684 break;
693 685
694 default: 686 default:
695 mu_diag_output (MU_DIAG_CRIT, _("%s:%d: INTERNAL ERROR (please report)"), 687 mu_diag_output (MU_DIAG_CRIT,
688 _("%s:%d: INTERNAL ERROR (please report)"),
696 __FILE__, __LINE__); 689 __FILE__, __LINE__);
697 abort (); /* should never happen */ 690 abort (); /* should never happen */
698 } 691 }
...@@ -860,15 +853,9 @@ static void ...@@ -860,15 +853,9 @@ static void
860 cond_msgset (struct parsebuf *pb, struct search_node *node, struct value *arg, 853 cond_msgset (struct parsebuf *pb, struct search_node *node, struct value *arg,
861 struct value *retval) 854 struct value *retval)
862 { 855 {
863 int n = arg[0].v.msgset.n; 856 int rc = mu_list_locate (arg[0].v.msgset, &pb->msgno, NULL);
864 size_t *set = arg[0].v.msgset.set;
865 int i, rc;
866
867 for (i = rc = 0; rc == 0 && i < n; i++)
868 rc = set[i] == pb->msgno;
869
870 retval->type = value_number; 857 retval->type = value_number;
871 retval->v.number = rc; 858 retval->v.number = rc == 0;
872 } 859 }
873 860
874 static void 861 static void
...@@ -1084,16 +1071,12 @@ static void ...@@ -1084,16 +1071,12 @@ static void
1084 cond_uid (struct parsebuf *pb, struct search_node *node, struct value *arg, 1071 cond_uid (struct parsebuf *pb, struct search_node *node, struct value *arg,
1085 struct value *retval) 1072 struct value *retval)
1086 { 1073 {
1087 int n = arg[0].v.msgset.n; 1074 int rc;
1088 size_t *set = arg[0].v.msgset.set;
1089 size_t uid = 0; 1075 size_t uid = 0;
1090 int i, rc;
1091 1076
1092 mu_message_get_uid (pb->msg, &uid); 1077 mu_message_get_uid (pb->msg, &uid);
1093 for (i = rc = 0; rc == 0 && i < n; i++) 1078 rc = mu_list_locate (arg[0].v.msgset, &pb->msgno, NULL);
1094 rc = set[i] == uid;
1095
1096 retval->type = value_number; 1079 retval->type = value_number;
1097 retval->v.number = rc; 1080 retval->v.number = rc == 0;
1098 } 1081 }
1099 1082
......
...@@ -25,8 +25,7 @@ struct store_parse_closure ...@@ -25,8 +25,7 @@ struct store_parse_closure
25 int ack; 25 int ack;
26 int type; 26 int type;
27 int isuid; 27 int isuid;
28 size_t *set; 28 mu_list_t msgnumlist;
29 int count;
30 }; 29 };
31 30
32 static int 31 static int
...@@ -36,6 +35,7 @@ store_thunk (imap4d_parsebuf_t p) ...@@ -36,6 +35,7 @@ store_thunk (imap4d_parsebuf_t p)
36 char *msgset; 35 char *msgset;
37 char *data; 36 char *data;
38 int status; 37 int status;
38 char *end;
39 39
40 msgset = imap4d_parsebuf_next (p, 1); 40 msgset = imap4d_parsebuf_next (p, 1);
41 data = imap4d_parsebuf_next (p, 1); 41 data = imap4d_parsebuf_next (p, 1);
...@@ -70,20 +70,10 @@ store_thunk (imap4d_parsebuf_t p) ...@@ -70,20 +70,10 @@ store_thunk (imap4d_parsebuf_t p)
70 } 70 }
71 71
72 /* Get the message numbers in set[]. */ 72 /* Get the message numbers in set[]. */
73 status = util_msgset (msgset, &pclos->set, &pclos->count, pclos->isuid); 73 status = util_parse_msgset (msgset, pclos->isuid, mbox,
74 switch (status) 74 &pclos->msgnumlist, &end);
75 { 75 if (status)
76 case 0:
77 break;
78
79 case EINVAL:
80 /* See RFC 3501, section 6.4.8, and a comment to the equivalent code
81 in fetch.c */
82 return RESP_OK;
83
84 default:
85 imap4d_parsebuf_exit (p, "Failed to parse message set"); 76 imap4d_parsebuf_exit (p, "Failed to parse message set");
86 }
87 77
88 if (p->token[0] != '(') 78 if (p->token[0] != '(')
89 imap4d_parsebuf_exit (p, "Syntax error"); 79 imap4d_parsebuf_exit (p, "Syntax error");
...@@ -101,70 +91,76 @@ store_thunk (imap4d_parsebuf_t p) ...@@ -101,70 +91,76 @@ store_thunk (imap4d_parsebuf_t p)
101 return RESP_OK; 91 return RESP_OK;
102 } 92 }
103 93
104 int 94 static int
105 imap4d_store0 (imap4d_tokbuf_t tok, int isuid, char **ptext) 95 _do_store (size_t msgno, void *data)
106 { 96 {
107 int rc; 97 struct store_parse_closure *pclos = data;
108 struct store_parse_closure pclos;
109
110 memset (&pclos, 0, sizeof pclos);
111 pclos.ack = 1;
112 pclos.isuid = isuid;
113
114 rc = imap4d_with_parsebuf (tok,
115 IMAP4_ARG_1 + !!isuid,
116 ".",
117 store_thunk, &pclos,
118 ptext);
119 if (rc == RESP_OK)
120 {
121 size_t i;
122
123 for (i = 0; i < pclos.count; i++)
124 {
125 mu_message_t msg = NULL; 98 mu_message_t msg = NULL;
126 mu_attribute_t attr = NULL; 99 mu_attribute_t attr = NULL;
127 size_t msgno = isuid ? uid_to_msgno (pclos.set[i]) : pclos.set[i];
128 100
129 if (msgno)
130 {
131 mu_mailbox_get_message (mbox, msgno, &msg); 101 mu_mailbox_get_message (mbox, msgno, &msg);
132 mu_message_get_attribute (msg, &attr); 102 mu_message_get_attribute (msg, &attr);
133 103
134 switch (pclos.how) 104 switch (pclos->how)
135 { 105 {
136 case STORE_ADD: 106 case STORE_ADD:
137 mu_attribute_set_flags (attr, pclos.type); 107 mu_attribute_set_flags (attr, pclos->type);
138 break; 108 break;
139 109
140 case STORE_UNSET: 110 case STORE_UNSET:
141 mu_attribute_unset_flags (attr, pclos.type); 111 mu_attribute_unset_flags (attr, pclos->type);
142 break; 112 break;
143 113
144 case STORE_SET: 114 case STORE_SET:
145 mu_attribute_unset_flags (attr, 0xffffffff); /* FIXME */ 115 mu_attribute_unset_flags (attr, 0xffffffff); /* FIXME */
146 mu_attribute_set_flags (attr, pclos.type); 116 mu_attribute_set_flags (attr, pclos->type);
147 }
148 } 117 }
149 118
150 if (pclos.ack) 119
120 if (pclos->ack)
151 { 121 {
152 io_sendf ("* %lu FETCH (", (unsigned long) msgno); 122 io_sendf ("* %lu FETCH (", (unsigned long) msgno);
153 123
154 if (isuid) 124 if (pclos->isuid)
155 io_sendf ("UID %lu ", (unsigned long) msgno); 125 {
126 size_t uid;
127 int rc = mu_mailbox_translate (mbox, MU_MAILBOX_UID_TO_MSGNO,
128 msgno, &uid);
129 if (rc == 0)
130 io_sendf ("UID %lu ", (unsigned long) uid);
131 }
156 io_sendf ("FLAGS ("); 132 io_sendf ("FLAGS (");
157 util_print_flags (attr); 133 util_print_flags (attr);
158 io_sendf ("))\n"); 134 io_sendf ("))\n");
159 } 135 }
160 /* Update the flags of uid table. */ 136 /* Update the flags of uid table. */
161 imap4d_sync_flags (pclos.set[i]); 137 imap4d_sync_flags (msgno);
162 } 138 return 0;
139 }
140
141 int
142 imap4d_store0 (imap4d_tokbuf_t tok, int isuid, char **ptext)
143 {
144 int rc;
145 struct store_parse_closure pclos;
146
147 memset (&pclos, 0, sizeof pclos);
148 pclos.ack = 1;
149 pclos.isuid = isuid;
150
151 rc = imap4d_with_parsebuf (tok,
152 IMAP4_ARG_1 + !!isuid,
153 ".",
154 store_thunk, &pclos,
155 ptext);
156 if (rc == RESP_OK)
157 {
158 util_foreach_message (pclos.msgnumlist, _do_store, &pclos);
163 159
164 *ptext = "Completed"; 160 *ptext = "Completed";
165 } 161 }
166 162
167 free (pclos.set); 163 mu_list_destroy (&pclos.msgnumlist);
168 164
169 return rc; 165 return rc;
170 } 166 }
......
...@@ -39,6 +39,11 @@ imap4d IMAP4D_OPTIONS < input | remove_uidvalidity | tr -d '\r' ...@@ -39,6 +39,11 @@ imap4d IMAP4D_OPTIONS < input | remove_uidvalidity | tr -d '\r'
39 * FLAGS (\Answered \Flagged \Deleted \Seen \Draft) 39 * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
40 * OK [[PERMANENTFLAGS (\Answered \Flagged \Deleted \Seen \Draft)]] Permanent flags 40 * OK [[PERMANENTFLAGS (\Answered \Flagged \Deleted \Seen \Draft)]] Permanent flags
41 1 OK [[READ-WRITE]] SELECT Completed 41 1 OK [[READ-WRITE]] SELECT Completed
42 * 1 FETCH (FLAGS (\Recent))
43 * 2 FETCH (FLAGS (\Recent))
44 * 3 FETCH (FLAGS (\Recent))
45 * 4 FETCH (FLAGS (\Recent))
46 * 5 FETCH (FLAGS (\Recent))
42 2 OK FETCH Completed 47 2 OK FETCH Completed
43 * 1 FETCH (UID 1 FLAGS (\Recent)) 48 * 1 FETCH (UID 1 FLAGS (\Recent))
44 * 2 FETCH (UID 2 FLAGS (\Recent)) 49 * 2 FETCH (UID 2 FLAGS (\Recent))
......
...@@ -141,13 +141,13 @@ int mu_list_get_iterator (mu_list_t _list, mu_iterator_t *_pitr); ...@@ -141,13 +141,13 @@ int mu_list_get_iterator (mu_list_t _list, mu_iterator_t *_pitr);
141 141
142 /* A general-purpose iteration function. When called, _item points to 142 /* A general-purpose iteration function. When called, _item points to
143 the item currently visited and _data points to call-specific data. */ 143 the item currently visited and _data points to call-specific data. */
144 typedef int mu_list_action_t (void *_item, void *_data); 144 typedef int (*mu_list_action_t) (void *_item, void *_data);
145 145
146 /* Execute _action for each element in _list. Use _data as the call-specific 146 /* Execute _action for each element in _list. Use _data as the call-specific
147 data. */ 147 data. */
148 int mu_list_foreach (mu_list_t _list, mu_list_action_t *_action, void *_data); 148 int mu_list_foreach (mu_list_t _list, mu_list_action_t _action, void *_data);
149 /* A historical alias to the above. */ 149 /* A historical alias to the above. */
150 int mu_list_do (mu_list_t, mu_list_action_t *, void *) MU_DEPRECATED; 150 int mu_list_do (mu_list_t, mu_list_action_t, void *) MU_DEPRECATED;
151 151
152 /* ************************************************* */ 152 /* ************************************************* */
153 /* Functions for combining two lists. */ 153 /* Functions for combining two lists. */
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
24 #include <mailutils/errno.h> 24 #include <mailutils/errno.h>
25 25
26 int 26 int
27 mu_list_foreach (mu_list_t list, mu_list_action_t *action, void *cbdata) 27 mu_list_foreach (mu_list_t list, mu_list_action_t action, void *cbdata)
28 { 28 {
29 mu_iterator_t itr; 29 mu_iterator_t itr;
30 int status = 0; 30 int status = 0;
...@@ -49,7 +49,7 @@ mu_list_foreach (mu_list_t list, mu_list_action_t *action, void *cbdata) ...@@ -49,7 +49,7 @@ mu_list_foreach (mu_list_t list, mu_list_action_t *action, void *cbdata)
49 /* Retained for compatibility with previous versions. 49 /* Retained for compatibility with previous versions.
50 In the future it will be removed, or changed to a define or weak alias. */ 50 In the future it will be removed, or changed to a define or weak alias. */
51 int 51 int
52 mu_list_do (mu_list_t list, mu_list_action_t *action, void *cbdata) 52 mu_list_do (mu_list_t list, mu_list_action_t action, void *cbdata)
53 { 53 {
54 return mu_list_foreach (list, action, cbdata); 54 return mu_list_foreach (list, action, cbdata);
55 } 55 }
......