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
451 additions
and
400 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 | 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 | ... | ... |
... | @@ -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)) | ... | ... |
... | @@ -17,10 +17,8 @@ | ... | @@ -17,10 +17,8 @@ |
17 | 17 | ||
18 | #include "imap4d.h" | 18 | #include "imap4d.h" |
19 | 19 | ||
20 | static int add2set (size_t **, int *, unsigned long); | ||
21 | |||
22 | /* NOTE: Allocates Memory. */ | 20 | /* NOTE: Allocates Memory. */ |
23 | /* Expand: ~ --> /home/user and to ~guest --> /home/guest. */ | 21 | /* Expand: ~ --> /home/user, and ~guest --> /home/guest. */ |
24 | char * | 22 | char * |
25 | util_tilde_expansion (const char *ref, const char *delim) | 23 | util_tilde_expansion (const char *ref, const char *delim) |
26 | { | 24 | { |
... | @@ -44,17 +42,7 @@ util_getfullpath (const char *name, const char *delim) | ... | @@ -44,17 +42,7 @@ util_getfullpath (const char *name, const char *delim) |
44 | return mu_normalize_path (p); | 42 | return mu_normalize_path (p); |
45 | } | 43 | } |
46 | 44 | ||
47 | static int | 45 | /* A message set is defined as: |
48 | comp_int (const void *a, const void *b) | ||
49 | { | ||
50 | return *(int *) a - *(int *) b; | ||
51 | } | ||
52 | |||
53 | /* Parse the message set specification from S. Store message numbers | ||
54 | in SET, store number of element in the SET into the memory pointed to | ||
55 | by N. | ||
56 | |||
57 | A message set is defined as: | ||
58 | 46 | ||
59 | set ::= sequence_num / (sequence_num ":" sequence_num) / (set "," set) | 47 | set ::= sequence_num / (sequence_num ":" sequence_num) / (set "," set) |
60 | sequence_num ::= nz_number / "*" | 48 | sequence_num ::= nz_number / "*" |
... | @@ -65,204 +53,301 @@ comp_int (const void *a, const void *b) | ... | @@ -65,204 +53,301 @@ comp_int (const void *a, const void *b) |
65 | ;; the mailbox. | 53 | ;; the mailbox. |
66 | nz_number ::= digit_nz *digit | 54 | nz_number ::= digit_nz *digit |
67 | 55 | ||
68 | FIXME: The message sets like <,,,> or <:12> or <20:10> are not considered | 56 | Non-existing sequence numbers are ignored, except when they form part |
69 | an error */ | 57 | of a message range (sequence_num ":" sequence_num), in which case they |
70 | int | 58 | are treated as minimal or maximal sequence numbers (uids) available in |
71 | util_msgset (char *s, size_t ** set, int *n, int isuid) | 59 | the mailbox depending on whether they appear to the left or to the right |
72 | { | 60 | of ":". |
73 | unsigned long val = 0; | 61 | */ |
74 | unsigned long low = 0; | 62 | |
75 | int done = 0; | 63 | /* Message set is a list of non-overlapping msgranges, in ascending |
76 | int status = 0; | 64 | order. No matter what command flavor is used, msg_beg and msg_end are |
77 | size_t max = 0; | 65 | treated as sequence numbers (not UIDs). */ |
78 | size_t *tmp; | 66 | struct msgrange |
79 | int i, j; | 67 | { |
80 | unsigned long invalid_uid = 0; /* For UID mode only: have we | 68 | size_t msg_beg; /* beginning message number */ |
81 | encountered an uid > max uid? */ | 69 | size_t msg_end; /* ending message number */ |
82 | 70 | }; | |
83 | status = mu_mailbox_messages_count (mbox, &max); | 71 | |
84 | if (status != 0) | 72 | /* Comparator function used to sort the message set list. */ |
85 | return status; | 73 | static int |
86 | /* If it is a uid sequence, override max with the UID. */ | 74 | compare_msgrange (const void *a, const void *b) |
87 | if (isuid) | 75 | { |
76 | struct msgrange const *sa = a; | ||
77 | struct msgrange const *sb = b; | ||
78 | |||
79 | if (sa->msg_beg < sb->msg_beg) | ||
80 | return -1; | ||
81 | if (sa->msg_beg > sb->msg_beg) | ||
82 | return 1; | ||
83 | if (sa->msg_end < sb->msg_end) | ||
84 | return -1; | ||
85 | if (sa->msg_end > sb->msg_end) | ||
86 | return 1; | ||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | /* Comparator function used to locate a message in the list. Second argument | ||
91 | (b) is a pointer to the message number. */ | ||
92 | static int | ||
93 | compare_msgnum (const void *a, const void *b) | ||
94 | { | ||
95 | struct msgrange const *range = a; | ||
96 | size_t msgno = *(size_t*)b; | ||
97 | |||
98 | if (range->msg_beg <= msgno && msgno <= range->msg_end) | ||
99 | return 0; | ||
100 | return 1; | ||
101 | } | ||
102 | |||
103 | /* This structure keeps parser state while parsing message set. */ | ||
104 | struct parse_msgnum_env | ||
105 | { | ||
106 | char *s; /* Current position in string */ | ||
107 | size_t minval; /* Min. sequence number or UID */ | ||
108 | size_t maxval; /* Max. sequence number or UID */ | ||
109 | mu_list_t list; /* List being built. */ | ||
110 | int isuid:1; /* True, if parsing an UID command. */ | ||
111 | mu_mailbox_t mailbox; /* Reference mailbox (can be NULL). */ | ||
112 | }; | ||
113 | |||
114 | /* Get a single message number/UID from env->s and store it into *PN. | ||
115 | Return 0 on success and error code on error. | ||
116 | |||
117 | Advance env->s to the point past the parsed message number. | ||
118 | */ | ||
119 | static int | ||
120 | get_msgnum (struct parse_msgnum_env *env, size_t *pn) | ||
121 | { | ||
122 | size_t msgnum; | ||
123 | char *p; | ||
124 | |||
125 | errno = 0; | ||
126 | msgnum = strtoul (env->s, &p, 10); | ||
127 | if (msgnum == ULONG_MAX && errno == ERANGE) | ||
128 | return MU_ERR_PARSE; | ||
129 | env->s = p; | ||
130 | if (msgnum > env->maxval) | ||
131 | msgnum = env->maxval; | ||
132 | *pn = msgnum; | ||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | /* Parse a single message range (A:B). Treat '*' as the largest number/UID | ||
137 | in use. */ | ||
138 | static int | ||
139 | parse_msgrange (struct parse_msgnum_env *env) | ||
140 | { | ||
141 | int rc; | ||
142 | struct msgrange msgrange, *mp; | ||
143 | |||
144 | if (*env->s == '*') | ||
88 | { | 145 | { |
89 | mu_message_t msg = NULL; | 146 | msgrange.msg_beg = env->maxval; |
90 | mu_mailbox_get_message (mbox, max, &msg); | 147 | env->s++; |
91 | mu_message_get_uid (msg, &max); | ||
92 | } | 148 | } |
149 | else if ((rc = get_msgnum (env, &msgrange.msg_beg))) | ||
150 | return rc; | ||
93 | 151 | ||
94 | *n = 0; | 152 | if (*env->s == ':') |
95 | *set = NULL; | ||
96 | while (*s) | ||
97 | { | 153 | { |
98 | switch (*s) | 154 | if (*++env->s == '*') |
99 | { | 155 | { |
100 | /* isdigit */ | 156 | msgrange.msg_end = env->maxval; |
101 | case '0': | 157 | ++env->s; |
102 | case '1': | ||
103 | case '2': | ||
104 | case '3': | ||
105 | case '4': | ||
106 | case '5': | ||
107 | case '6': | ||
108 | case '7': | ||
109 | case '8': | ||
110 | case '9': | ||
111 | { | ||
112 | errno = 0; | ||
113 | val = strtoul (s, &s, 10); | ||
114 | if (val == ULONG_MAX && errno == ERANGE) | ||
115 | { | ||
116 | if (*set) | ||
117 | free (*set); | ||
118 | *set = NULL; | ||
119 | *n = 0; | ||
120 | return EINVAL; | ||
121 | } | 158 | } |
122 | else if (val > max) | 159 | else if (*env->s == 0) |
160 | return MU_ERR_PARSE; | ||
161 | else if ((rc = get_msgnum (env, &msgrange.msg_end))) | ||
162 | return rc; | ||
163 | } | ||
164 | else | ||
165 | msgrange.msg_end = msgrange.msg_beg; | ||
166 | |||
167 | if (msgrange.msg_end < msgrange.msg_beg) | ||
123 | { | 168 | { |
124 | if (isuid) | 169 | size_t tmp = msgrange.msg_end; |
170 | msgrange.msg_end = msgrange.msg_beg; | ||
171 | msgrange.msg_beg = tmp; | ||
172 | } | ||
173 | |||
174 | if (env->isuid && env->mailbox) | ||
125 | { | 175 | { |
126 | invalid_uid = 1; | 176 | int rc; |
127 | continue; | 177 | |
178 | rc = mu_mailbox_translate (env->mailbox, | ||
179 | MU_MAILBOX_UID_TO_MSGNO, | ||
180 | msgrange.msg_beg, &msgrange.msg_beg); | ||
181 | if (rc == MU_ERR_NOENT) | ||
182 | msgrange.msg_beg = env->minval; | ||
183 | else if (rc) | ||
184 | return rc; | ||
185 | |||
186 | rc = mu_mailbox_translate (env->mailbox, | ||
187 | MU_MAILBOX_UID_TO_MSGNO, | ||
188 | msgrange.msg_end, &msgrange.msg_end); | ||
189 | if (rc == MU_ERR_NOENT) | ||
190 | msgrange.msg_end = env->maxval; | ||
191 | else if (rc) | ||
192 | return rc; | ||
128 | } | 193 | } |
129 | if (*set) | 194 | |
130 | free (*set); | 195 | mp = malloc (sizeof (*mp)); |
131 | *set = NULL; | 196 | if (!mp) |
132 | *n = 0; | 197 | return ENOMEM; |
198 | |||
199 | *mp = msgrange; | ||
200 | |||
201 | rc = mu_list_append (env->list, mp); | ||
202 | if (rc) | ||
203 | free (mp); | ||
204 | return rc; | ||
205 | } | ||
206 | |||
207 | /* Parse message set specification S. ISUID indicates if the set is supposed | ||
208 | to contain UIDs (they will be translated to message sequence numbers). | ||
209 | MBX is a reference mailbox or NULL. | ||
210 | |||
211 | On success, return 0 and place the resulting set into *PLIST. On error, | ||
212 | return error code and point END to the position in the input string where | ||
213 | the parsing failed. */ | ||
214 | int | ||
215 | util_parse_msgset (char *s, int isuid, mu_mailbox_t mbx, | ||
216 | mu_list_t *plist, char **end) | ||
217 | { | ||
218 | int rc; | ||
219 | struct parse_msgnum_env env; | ||
220 | size_t n; | ||
221 | |||
222 | if (!s) | ||
133 | return EINVAL; | 223 | return EINVAL; |
134 | } | 224 | if (!*s) |
225 | return MU_ERR_PARSE; | ||
135 | 226 | ||
136 | if (low) | 227 | memset (&env, 0, sizeof (env)); |
137 | { | 228 | env.s = s; |
138 | /* Reverse it. */ | 229 | if (mbox) |
139 | if (low > val) | ||
140 | { | 230 | { |
141 | long tmp = low; | 231 | size_t lastmsgno; /* Max. sequence number. */ |
142 | tmp -= 2; | 232 | |
143 | if (tmp < 0 || val == 0) | 233 | rc = mu_mailbox_messages_count (mbx, &lastmsgno); |
234 | if (rc == 0) | ||
144 | { | 235 | { |
145 | free (*set); | 236 | if (isuid) |
146 | *set = NULL; | ||
147 | *n = 0; | ||
148 | return EINVAL; | ||
149 | } | ||
150 | low = val; | ||
151 | val = tmp; | ||
152 | } | ||
153 | for (; low && low <= val; low++) | ||
154 | { | 237 | { |
155 | status = add2set (set, n, low); | 238 | rc = mu_mailbox_translate (mbox, MU_MAILBOX_MSGNO_TO_UID, |
156 | if (status != 0) | 239 | lastmsgno, &env.maxval); |
157 | return status; | 240 | if (rc == 0) |
158 | } | 241 | rc = mu_mailbox_translate (mbox, MU_MAILBOX_MSGNO_TO_UID, |
159 | low = 0; | 242 | 1, &env.minval); |
160 | } | 243 | } |
161 | else | 244 | else |
162 | { | 245 | env.maxval = lastmsgno; |
163 | status = add2set (set, n, val); | ||
164 | if (status != 0) | ||
165 | return status; | ||
166 | } | 246 | } |
167 | break; | 247 | if (rc) |
248 | return rc; | ||
168 | } | 249 | } |
250 | rc = mu_list_create (&env.list); | ||
251 | if (rc) | ||
252 | return rc; | ||
253 | mu_list_set_destroy_item (env.list, mu_list_free_item); | ||
169 | 254 | ||
170 | /* A pair of numbers separated by a ':' character indicates a | 255 | env.isuid = isuid; |
171 | contiguous set of mesages ranging from the first number to the | 256 | env.mailbox = mbx; |
172 | second: | ||
173 | 3:5 --> 3 4 5 | ||
174 | */ | ||
175 | case ':': | ||
176 | low = val + 1; | ||
177 | s++; | ||
178 | break; | ||
179 | 257 | ||
180 | /* As a convenience. '*' is provided to refer to the highest | 258 | while ((rc = parse_msgrange (&env)) == 0 && *env.s) |
181 | message number int the mailbox: | ||
182 | 5:* --> 5 6 7 8 | ||
183 | */ | ||
184 | case '*': | ||
185 | { | 259 | { |
186 | val = max; | 260 | if (*env.s != ',' || *++env.s == 0) |
187 | s++; | 261 | { |
188 | status = add2set (set, n, val); | 262 | rc = MU_ERR_PARSE; |
189 | if (status != 0) | ||
190 | return status; | ||
191 | } | ||
192 | break; | ||
193 | |||
194 | /* IMAP also allows a set of noncontiguous numbers to be specified | ||
195 | with the ',' character: | ||
196 | 1,3,5,7 --> 1 3 5 7 | ||
197 | */ | ||
198 | case ',': | ||
199 | s++; | ||
200 | break; | 263 | break; |
264 | } | ||
265 | } | ||
201 | 266 | ||
202 | default: | 267 | if (end) |
203 | done = 1; | 268 | *end = env.s; |
204 | if (*set) | 269 | if (rc) |
205 | free (*set); | 270 | { |
206 | *set = NULL; | 271 | mu_list_destroy (&env.list); |
207 | *n = 0; | 272 | return rc; |
208 | return EINVAL; | 273 | } |
209 | |||
210 | } /* switch */ | ||
211 | 274 | ||
212 | if (done) | 275 | mu_list_count (env.list, &n); |
213 | break; | 276 | if (n > 1) |
214 | } /* while */ | 277 | { |
278 | mu_iterator_t itr; | ||
279 | struct msgrange *last = NULL; | ||
215 | 280 | ||
216 | if (*n == 0) | 281 | /* Sort the list and coalesce overlapping message ranges. */ |
217 | return 0; | 282 | mu_list_sort (env.list, compare_msgrange); |
218 | 283 | ||
219 | /* For message sets in form X:Y where Y is a not-existing UID greater | 284 | mu_list_get_iterator (env.list, &itr); |
220 | than max UID, replace Y with the max UID in the mailbox */ | 285 | for (mu_iterator_first (itr); !mu_iterator_is_done (itr); |
221 | if (*n == 1 && invalid_uid) | 286 | mu_iterator_next (itr)) |
222 | { | 287 | { |
223 | val = max; | 288 | struct msgrange *msgrange; |
224 | status = add2set (set, n, val); | ||
225 | if (status != 0) | ||
226 | return status; | ||
227 | } | ||
228 | 289 | ||
229 | if (low) | 290 | mu_iterator_current (itr, (void**)&msgrange); |
230 | { | 291 | if (last) |
231 | /* Reverse it. */ | ||
232 | if (low > val) | ||
233 | { | 292 | { |
234 | long tmp = low; | 293 | if (last->msg_beg <= msgrange->msg_beg && |
235 | tmp -= 2; | 294 | msgrange->msg_beg <= last->msg_end) |
236 | if (tmp < 0 || val == 0) | ||
237 | { | 295 | { |
238 | free (*set); | 296 | if (msgrange->msg_end > last->msg_end) |
239 | *set = NULL; | 297 | last->msg_end = msgrange->msg_end; |
240 | *n = 0; | 298 | mu_iterator_ctl (itr, mu_itrctl_delete, NULL); |
241 | return EINVAL; | 299 | continue; |
242 | } | 300 | } |
243 | low = val; | 301 | |
244 | val = tmp; | ||
245 | } | 302 | } |
246 | for (; low && low <= val; low++) | 303 | last = msgrange; |
247 | { | ||
248 | status = add2set (set, n, low); | ||
249 | if (status != 0) | ||
250 | return status; | ||
251 | } | 304 | } |
305 | mu_iterator_destroy (&itr); | ||
306 | } | ||
307 | |||
308 | if (rc == 0) | ||
309 | { | ||
310 | mu_list_set_comparator (env.list, compare_msgnum); | ||
311 | *plist = env.list; | ||
252 | } | 312 | } |
313 | else | ||
314 | mu_list_destroy (&env.list); | ||
315 | return rc; | ||
316 | } | ||
317 | |||
318 | struct action_closure | ||
319 | { | ||
320 | imap4d_message_action_t action; | ||
321 | void *data; | ||
322 | }; | ||
253 | 323 | ||
254 | /* Sort the resulting message set */ | 324 | static int |
255 | qsort (*set, *n, sizeof (**set), comp_int); | 325 | procrange (void *item, void *data) |
326 | { | ||
327 | struct msgrange *mp = item; | ||
328 | struct action_closure *clos = data; | ||
329 | size_t i; | ||
256 | 330 | ||
257 | /* Remove duplicates. tmp serves to avoid extra dereferences */ | 331 | for (i = mp->msg_beg; i <= mp->msg_end; i++) |
258 | tmp = *set; | 332 | { |
259 | for (i = 0, j = 1; i < *n; i++) | 333 | int rc = clos->action (i, clos->data); |
260 | if (tmp[j - 1] != tmp[i]) | 334 | if (rc) |
261 | tmp[j++] = tmp[i]; | 335 | return rc; |
262 | *n = j; | 336 | } |
263 | return 0; | 337 | return 0; |
264 | } | 338 | } |
265 | 339 | ||
340 | /* Apply ACTION to each message number from LIST. */ | ||
341 | int | ||
342 | util_foreach_message (mu_list_t list, imap4d_message_action_t action, | ||
343 | void *data) | ||
344 | { | ||
345 | struct action_closure clos; | ||
346 | clos.action = action; | ||
347 | clos.data = data; | ||
348 | return mu_list_foreach (list, procrange, &clos); | ||
349 | } | ||
350 | |||
266 | int | 351 | int |
267 | util_do_command (imap4d_tokbuf_t tok) | 352 | util_do_command (imap4d_tokbuf_t tok) |
268 | { | 353 | { |
... | @@ -317,26 +402,9 @@ util_getcommand (char *cmd, struct imap4d_command command_table[]) | ... | @@ -317,26 +402,9 @@ util_getcommand (char *cmd, struct imap4d_command command_table[]) |
317 | return NULL; | 402 | return NULL; |
318 | } | 403 | } |
319 | 404 | ||
320 | static int | ||
321 | add2set (size_t ** set, int *n, unsigned long val) | ||
322 | { | ||
323 | size_t *tmp; | ||
324 | tmp = realloc (*set, (*n + 1) * sizeof (**set)); | ||
325 | if (tmp == NULL) | ||
326 | { | ||
327 | if (*set) | ||
328 | free (*set); | ||
329 | *n = 0; | ||
330 | return ENOMEM; | ||
331 | } | ||
332 | *set = tmp; | ||
333 | (*set)[*n] = val; | ||
334 | (*n)++; | ||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | static void | 405 | static void |
339 | adjust_tm (struct tm *tm, struct mu_timezone *tz, enum datetime_parse_mode flag) | 406 | adjust_tm (struct tm *tm, struct mu_timezone *tz, |
407 | enum datetime_parse_mode flag) | ||
340 | { | 408 | { |
341 | switch (flag) | 409 | switch (flag) |
342 | { | 410 | { | ... | ... |
... | @@ -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