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.
Showing
10 changed files
with
223 additions
and
240 deletions
... | @@ -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 | struct copy_env | ||
59 | { | ||
60 | mu_mailbox_t dst; | ||
61 | mu_mailbox_t src; | ||
62 | mu_off_t total; | ||
63 | int ret; | ||
64 | char **err_text; | ||
65 | }; | ||
66 | |||
58 | static int | 67 | static int |
59 | copy_check_size (mu_mailbox_t mbox, size_t n, size_t *set, mu_off_t *size) | 68 | size_sum (size_t msgno, void *data) |
60 | { | 69 | { |
61 | int status; | 70 | struct copy_env *env = data; |
62 | size_t i; | 71 | mu_message_t msg = NULL; |
63 | mu_off_t total = 0; | 72 | int rc; |
64 | 73 | ||
65 | for (i = 0; i < n; i++) | 74 | rc = mu_mailbox_get_message (env->src, msgno, &msg); |
75 | if (rc) | ||
76 | { | ||
77 | mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_get_message", NULL, rc); | ||
78 | env->ret = RESP_NO; | ||
79 | return MU_ERR_FAILURE; | ||
80 | } | ||
81 | else | ||
66 | { | 82 | { |
67 | mu_message_t msg = NULL; | 83 | size_t size; |
68 | size_t msgno = set[i]; | 84 | rc = mu_message_size (msg, &size); |
69 | if (msgno) | 85 | if (rc) |
70 | { | 86 | { |
71 | status = mu_mailbox_get_message (mbox, msgno, &msg); | 87 | mu_diag_funcall (MU_DIAG_ERROR, "mu_message_size", NULL, rc); |
72 | if (status) | 88 | env->ret = RESP_BAD; |
73 | { | 89 | return MU_ERR_FAILURE; |
74 | mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_get_message", NULL, | ||
75 | status); | ||
76 | return RESP_BAD; | ||
77 | } | ||
78 | else | ||
79 | { | ||
80 | size_t size; | ||
81 | status = mu_message_size (msg, &size); | ||
82 | if (status) | ||
83 | { | ||
84 | mu_diag_funcall (MU_DIAG_ERROR, "mu_message_size", NULL, | ||
85 | status); | ||
86 | return RESP_BAD; | ||
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; | 100 | mu_message_t msg = NULL; |
101 | mu_off_t total; | 101 | int status; |
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; | ||
110 | size_t msgno = set[i]; | ||
111 | 102 | ||
112 | if (msgno) | 103 | status = mu_mailbox_get_message (env->src, msgno, &msg); |
113 | { | 104 | if (status) |
114 | int status = mu_mailbox_get_message (src, msgno, &msg); | 105 | { |
115 | if (status) | 106 | mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_get_message", NULL, |
116 | { | 107 | status); |
117 | mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_get_message", NULL, | 108 | env->ret = RESP_BAD; |
118 | status); | 109 | return MU_ERR_FAILURE; |
119 | return RESP_BAD; | 110 | } |
120 | } | ||
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, | 118 | status); |
129 | status); | 119 | env->ret = RESP_BAD; |
130 | return RESP_BAD; | 120 | return MU_ERR_FAILURE; |
131 | } | ||
132 | } | ||
133 | } | 121 | } |
134 | quota_update (total); | 122 | |
135 | return RESP_OK; | 123 | return 0; |
136 | } | 124 | } |
137 | 125 | ||
138 | static int | 126 | static int |
139 | safe_copy (mu_mailbox_t dst, mu_mailbox_t src, size_t n, size_t *set, | 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; | ||
152 | } | ||
153 | env.total = 0; | ||
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; | ||
159 | } | ||
160 | |||
161 | static int | ||
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,23 +248,14 @@ imap4d_copy0 (imap4d_tokbuf_t tok, int isuid, char **err_text) | ... | @@ -230,23 +248,14 @@ 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 | { | 253 | { |
236 | /* See RFC 3501, section 6.4.8, and a comment to the equivalent code | 254 | *err_text = "Error parsing message set"; |
237 | in fetch.c */ | 255 | /* FIXME: print error location */ |
238 | *err_text = "Completed"; | 256 | return RESP_BAD; |
239 | return RESP_OK; | ||
240 | } | 257 | } |
241 | 258 | ||
242 | if (isuid) | ||
243 | { | ||
244 | int i; | ||
245 | /* Fixup the message set. Perhaps util_msgset should do it itself? */ | ||
246 | for (i = 0; i < n; i++) | ||
247 | set[i] = uid_to_msgno (set[i]); | ||
248 | } | ||
249 | |||
250 | mailbox_name = namespace_getfullpath (name, delim, &ns); | 259 | mailbox_name = namespace_getfullpath (name, delim, &ns); |
251 | 260 | ||
252 | if (!mailbox_name) | 261 | if (!mailbox_name) |
... | @@ -256,7 +265,7 @@ imap4d_copy0 (imap4d_tokbuf_t tok, int isuid, char **err_text) | ... | @@ -256,7 +265,7 @@ imap4d_copy0 (imap4d_tokbuf_t tok, int isuid, char **err_text) |
256 | } | 265 | } |
257 | 266 | ||
258 | /* If the destination mailbox does not exist, a server should return | 267 | /* If the destination mailbox does not exist, a server should return |
259 | an error. */ | 268 | an error. */ |
260 | status = mu_mailbox_create_default (&cmbox, mailbox_name); | 269 | status = mu_mailbox_create_default (&cmbox, mailbox_name); |
261 | if (status == 0) | 270 | if (status == 0) |
262 | { | 271 | { |
... | @@ -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: | 1764 | imap4d_parsebuf_exit (pb, "Failed to parse message set"); |
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"); | ||
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 | ... | ... |
... | @@ -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,20 +673,20 @@ parse_simple_key (struct parsebuf *pb) | ... | @@ -680,20 +673,20 @@ 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, |
696 | __FILE__, __LINE__); | 688 | _("%s:%d: INTERNAL ERROR (please report)"), |
689 | __FILE__, __LINE__); | ||
697 | abort (); /* should never happen */ | 690 | abort (); /* should never happen */ |
698 | } | 691 | } |
699 | node->v.key.arg[node->v.key.narg++] = arg; | 692 | node->v.key.arg[node->v.key.narg++] = arg; |
... | @@ -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: | 76 | imap4d_parsebuf_exit (p, "Failed to parse message set"); |
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"); | ||
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,12 +91,59 @@ store_thunk (imap4d_parsebuf_t p) | ... | @@ -101,12 +91,59 @@ store_thunk (imap4d_parsebuf_t p) |
101 | return RESP_OK; | 91 | return RESP_OK; |
102 | } | 92 | } |
103 | 93 | ||
94 | static int | ||
95 | _do_store (size_t msgno, void *data) | ||
96 | { | ||
97 | struct store_parse_closure *pclos = data; | ||
98 | mu_message_t msg = NULL; | ||
99 | mu_attribute_t attr = NULL; | ||
100 | |||
101 | mu_mailbox_get_message (mbox, msgno, &msg); | ||
102 | mu_message_get_attribute (msg, &attr); | ||
103 | |||
104 | switch (pclos->how) | ||
105 | { | ||
106 | case STORE_ADD: | ||
107 | mu_attribute_set_flags (attr, pclos->type); | ||
108 | break; | ||
109 | |||
110 | case STORE_UNSET: | ||
111 | mu_attribute_unset_flags (attr, pclos->type); | ||
112 | break; | ||
113 | |||
114 | case STORE_SET: | ||
115 | mu_attribute_unset_flags (attr, 0xffffffff); /* FIXME */ | ||
116 | mu_attribute_set_flags (attr, pclos->type); | ||
117 | } | ||
118 | |||
119 | |||
120 | if (pclos->ack) | ||
121 | { | ||
122 | io_sendf ("* %lu FETCH (", (unsigned long) msgno); | ||
123 | |||
124 | if (pclos->isuid) | ||
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 | } | ||
132 | io_sendf ("FLAGS ("); | ||
133 | util_print_flags (attr); | ||
134 | io_sendf ("))\n"); | ||
135 | } | ||
136 | /* Update the flags of uid table. */ | ||
137 | imap4d_sync_flags (msgno); | ||
138 | return 0; | ||
139 | } | ||
140 | |||
104 | int | 141 | int |
105 | imap4d_store0 (imap4d_tokbuf_t tok, int isuid, char **ptext) | 142 | imap4d_store0 (imap4d_tokbuf_t tok, int isuid, char **ptext) |
106 | { | 143 | { |
107 | int rc; | 144 | int rc; |
108 | struct store_parse_closure pclos; | 145 | struct store_parse_closure pclos; |
109 | 146 | ||
110 | memset (&pclos, 0, sizeof pclos); | 147 | memset (&pclos, 0, sizeof pclos); |
111 | pclos.ack = 1; | 148 | pclos.ack = 1; |
112 | pclos.isuid = isuid; | 149 | pclos.isuid = isuid; |
... | @@ -118,53 +155,12 @@ imap4d_store0 (imap4d_tokbuf_t tok, int isuid, char **ptext) | ... | @@ -118,53 +155,12 @@ imap4d_store0 (imap4d_tokbuf_t tok, int isuid, char **ptext) |
118 | ptext); | 155 | ptext); |
119 | if (rc == RESP_OK) | 156 | if (rc == RESP_OK) |
120 | { | 157 | { |
121 | size_t i; | 158 | util_foreach_message (pclos.msgnumlist, _do_store, &pclos); |
122 | 159 | ||
123 | for (i = 0; i < pclos.count; i++) | ||
124 | { | ||
125 | mu_message_t msg = NULL; | ||
126 | mu_attribute_t attr = NULL; | ||
127 | size_t msgno = isuid ? uid_to_msgno (pclos.set[i]) : pclos.set[i]; | ||
128 | |||
129 | if (msgno) | ||
130 | { | ||
131 | mu_mailbox_get_message (mbox, msgno, &msg); | ||
132 | mu_message_get_attribute (msg, &attr); | ||
133 | |||
134 | switch (pclos.how) | ||
135 | { | ||
136 | case STORE_ADD: | ||
137 | mu_attribute_set_flags (attr, pclos.type); | ||
138 | break; | ||
139 | |||
140 | case STORE_UNSET: | ||
141 | mu_attribute_unset_flags (attr, pclos.type); | ||
142 | break; | ||
143 | |||
144 | case STORE_SET: | ||
145 | mu_attribute_unset_flags (attr, 0xffffffff); /* FIXME */ | ||
146 | mu_attribute_set_flags (attr, pclos.type); | ||
147 | } | ||
148 | } | ||
149 | |||
150 | if (pclos.ack) | ||
151 | { | ||
152 | io_sendf ("* %lu FETCH (", (unsigned long) msgno); | ||
153 | |||
154 | if (isuid) | ||
155 | io_sendf ("UID %lu ", (unsigned long) msgno); | ||
156 | io_sendf ("FLAGS ("); | ||
157 | util_print_flags (attr); | ||
158 | io_sendf ("))\n"); | ||
159 | } | ||
160 | /* Update the flags of uid table. */ | ||
161 | imap4d_sync_flags (pclos.set[i]); | ||
162 | } | ||
163 | |||
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)) | ... | ... |
This diff is collapsed.
Click to expand it.
... | @@ -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 | } | ... | ... |
-
Please register or sign in to post a comment