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))
......
...@@ -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 }
......