imap4d: fix list
* imap4d/list.c (refinfo) <refptr>: const pointer. (list_fun): Don't insert spurious delimiter between the prefix and the value. (list_ref): New static function. Actually list a normalized ref/wcard pair. (imap4d_list): Correctly handle wildcard part starting with namespace prefixes * imap4d/namespace.c (prefix_translate_name): If a prefix ends with a delimiter, allow its use without trailing delimiter in ref. E.g. LIST "~" "%" for prefix "~/" is quite OK. * imap4d/tests/atlocal.in: Fix prefix declaration.
Showing
3 changed files
with
117 additions
and
90 deletions
... | @@ -21,10 +21,10 @@ | ... | @@ -21,10 +21,10 @@ |
21 | 21 | ||
22 | struct refinfo | 22 | struct refinfo |
23 | { | 23 | { |
24 | char *refptr; /* Original reference */ | 24 | char const *refptr; /* Original reference */ |
25 | size_t reflen; /* Length of the original reference */ | 25 | size_t reflen; /* Length of the original reference */ |
26 | struct namespace_prefix const *pfx; | 26 | struct namespace_prefix const *pfx; |
27 | size_t dirlen; /* Length of the current directory prefix */ | 27 | size_t dirlen; /* Length of the current directory prefix */ |
28 | char *buf; | 28 | char *buf; |
29 | size_t bufsize; | 29 | size_t bufsize; |
30 | }; | 30 | }; |
... | @@ -78,7 +78,7 @@ list_fun (mu_folder_t folder, struct mu_list_response *resp, void *data) | ... | @@ -78,7 +78,7 @@ list_fun (mu_folder_t folder, struct mu_list_response *resp, void *data) |
78 | if (refinfo->refptr[0]) | 78 | if (refinfo->refptr[0]) |
79 | { | 79 | { |
80 | p = mu_stpcpy (refinfo->buf, refinfo->refptr); | 80 | p = mu_stpcpy (refinfo->buf, refinfo->refptr); |
81 | if (refinfo->refptr[refinfo->reflen-1] != refinfo->pfx->delim) | 81 | if (refinfo->reflen == strlen (refinfo->pfx->prefix) - 1) |
82 | *p++ = refinfo->pfx->delim; | 82 | *p++ = refinfo->pfx->delim; |
83 | } | 83 | } |
84 | else | 84 | else |
... | @@ -95,11 +95,72 @@ list_fun (mu_folder_t folder, struct mu_list_response *resp, void *data) | ... | @@ -95,11 +95,72 @@ list_fun (mu_folder_t folder, struct mu_list_response *resp, void *data) |
95 | else | 95 | else |
96 | { | 96 | { |
97 | io_send_qstring (name); | 97 | io_send_qstring (name); |
98 | io_sendf ("\n", name); | 98 | io_sendf ("\n"); |
99 | } | 99 | } |
100 | return 0; | 100 | return 0; |
101 | } | 101 | } |
102 | 102 | ||
103 | static int | ||
104 | list_ref (char const *ref, char const *wcard, char const *cwd, | ||
105 | struct namespace_prefix const *pfx) | ||
106 | { | ||
107 | int rc; | ||
108 | struct refinfo refinfo; | ||
109 | mu_folder_t folder; | ||
110 | char const *dir; | ||
111 | mu_url_t url; | ||
112 | |||
113 | if (pfx->ns == NS_OTHER && strcmp (ref, pfx->prefix) == 0 | ||
114 | && strpbrk (wcard, "*%")) | ||
115 | { | ||
116 | /* [A] server MAY return NO to such a LIST command, requiring that a | ||
117 | user name be included with the Other Users' Namespace prefix | ||
118 | before listing any other user's mailboxes */ | ||
119 | return RESP_NO; | ||
120 | } | ||
121 | |||
122 | rc = mu_folder_create (&folder, cwd); | ||
123 | if (rc) | ||
124 | { | ||
125 | return RESP_NO; | ||
126 | } | ||
127 | /* Force the right matcher */ | ||
128 | mu_folder_set_match (folder, mu_folder_imap_match); | ||
129 | |||
130 | memset (&refinfo, 0, sizeof refinfo); | ||
131 | |||
132 | /* Note: original reference would always coincide with the pfx->prefix, | ||
133 | if it weren't for the special handling of NS_OTHER namespace, where | ||
134 | the part between the prefix and the first delimiter is considered to | ||
135 | be a user name and is handled as part of the actual prefix. */ | ||
136 | refinfo.refptr = ref; | ||
137 | refinfo.reflen = strlen (ref); | ||
138 | refinfo.pfx = pfx; | ||
139 | |||
140 | mu_folder_get_url (folder, &url); | ||
141 | mu_url_sget_path (url, &dir); | ||
142 | refinfo.dirlen = strlen (dir); | ||
143 | |||
144 | /* The special name INBOX is included in the output from LIST, if | ||
145 | INBOX is supported by this server for this user and if the | ||
146 | uppercase string "INBOX" matches the interpreted reference and | ||
147 | mailbox name arguments with wildcards as described above. The | ||
148 | criteria for omitting INBOX is whether SELECT INBOX will return | ||
149 | failure; it is not relevant whether the user's real INBOX resides | ||
150 | on this or some other server. */ | ||
151 | |||
152 | if (!*ref && | ||
153 | (mu_imap_wildmatch (wcard, "INBOX", MU_HIERARCHY_DELIMITER) == 0 | ||
154 | || mu_imap_wildmatch (wcard, "inbox", MU_HIERARCHY_DELIMITER) == 0)) | ||
155 | io_untagged_response (RESP_NONE, "LIST (\\NoInferiors) NIL INBOX"); | ||
156 | |||
157 | mu_folder_enumerate (folder, NULL, (void*) wcard, 0, 0, NULL, | ||
158 | list_fun, &refinfo); | ||
159 | mu_folder_destroy (&folder); | ||
160 | free (refinfo.buf); | ||
161 | return RESP_OK; | ||
162 | } | ||
163 | |||
103 | /* | 164 | /* |
104 | 6.3.8. LIST Command | 165 | 6.3.8. LIST Command |
105 | 166 | ||
... | @@ -139,6 +200,12 @@ imap4d_list (struct imap4d_session *session, | ... | @@ -139,6 +200,12 @@ imap4d_list (struct imap4d_session *session, |
139 | { | 200 | { |
140 | char *ref; | 201 | char *ref; |
141 | char *wcard; | 202 | char *wcard; |
203 | int status = RESP_OK; | ||
204 | static char *resp_text[] = { | ||
205 | [RESP_OK] = "Completed", | ||
206 | [RESP_NO] = "The requested item could not be found", | ||
207 | [RESP_BAD] = "System error" | ||
208 | }; | ||
142 | 209 | ||
143 | if (imap4d_tokbuf_argc (tok) != 4) | 210 | if (imap4d_tokbuf_argc (tok) != 4) |
144 | return io_completion_response (command, RESP_BAD, "Invalid arguments"); | 211 | return io_completion_response (command, RESP_BAD, "Invalid arguments"); |
... | @@ -169,104 +236,56 @@ imap4d_list (struct imap4d_session *session, | ... | @@ -169,104 +236,56 @@ imap4d_list (struct imap4d_session *session, |
169 | } | 236 | } |
170 | else | 237 | else |
171 | { | 238 | { |
172 | int status; | 239 | char *cwd = NULL; |
173 | mu_folder_t folder; | ||
174 | mu_url_t url; | ||
175 | char *cwd; | ||
176 | char const *dir; | ||
177 | struct refinfo refinfo; | ||
178 | size_t i; | 240 | size_t i; |
179 | struct namespace_prefix const *pfx; | 241 | struct namespace_prefix const *pfx; |
180 | 242 | ||
181 | cwd = namespace_translate_name (ref, 0, &pfx); | 243 | if (ref[0] == 0) |
182 | if (!cwd) | ||
183 | { | 244 | { |
184 | return io_completion_response (command, RESP_NO, | 245 | cwd = namespace_translate_name (wcard, 0, &pfx); |
185 | "The requested item could not be found."); | 246 | if (cwd) |
247 | { | ||
248 | char *p = wcard + strlen (pfx->prefix); | ||
249 | ref = mu_strdup (pfx->prefix); | ||
250 | memmove (wcard, p, strlen (p) + 1); | ||
251 | free (cwd); | ||
252 | } | ||
253 | else | ||
254 | ref = mu_strdup (ref); | ||
186 | } | 255 | } |
187 | free (cwd); | 256 | else |
188 | 257 | ref = mu_strdup (ref); | |
189 | ref = mu_strdup (ref); | ||
190 | 258 | ||
191 | /* Find the longest directory prefix */ | 259 | /* Find the longest directory prefix */ |
192 | i = strcspn (wcard, "%*"); | 260 | i = strcspn (wcard, "%*"); |
193 | while (i > 0 && wcard[i - 1] != pfx->delim) | 261 | if (wcard[i]) |
194 | i--; | ||
195 | /* Append it to the reference */ | ||
196 | if (i) | ||
197 | { | 262 | { |
198 | size_t reflen = strlen (ref); | 263 | while (i > 0 && wcard[i - 1] != pfx->delim) |
199 | size_t len = i + reflen; | 264 | i--; |
200 | 265 | /* Append it to the reference */ | |
201 | ref = mu_realloc (ref, len); | 266 | if (i) |
202 | memcpy (ref + reflen, wcard, i - 1); /* omit the trailing / */ | 267 | { |
203 | ref[len-1] = 0; | 268 | size_t reflen = strlen (ref); |
204 | 269 | size_t len = i + reflen; | |
205 | wcard += i; | 270 | |
271 | ref = mu_realloc (ref, len); | ||
272 | memcpy (ref + reflen, wcard, i - 1); /* omit the trailing / */ | ||
273 | ref[len-1] = 0; | ||
274 | |||
275 | wcard += i; | ||
276 | } | ||
206 | } | 277 | } |
207 | 278 | ||
208 | cwd = namespace_translate_name (ref, 0, &pfx); | 279 | cwd = namespace_translate_name (ref, 0, &pfx); |
209 | if (!cwd) | 280 | if (cwd) |
210 | { | 281 | status = list_ref (ref, wcard, cwd, pfx); |
211 | free (ref); | 282 | else |
212 | return io_completion_response (command, RESP_NO, | 283 | status = RESP_NO; |
213 | "The requested item could not be found."); | ||
214 | } | ||
215 | |||
216 | if (pfx->ns == NS_OTHER | ||
217 | && strcmp (ref, pfx->prefix) == 0 | ||
218 | && strpbrk (wcard, "*%")) | ||
219 | { | ||
220 | /* [A] server MAY return NO to such a LIST command, requiring that a | ||
221 | user name be included with the Other Users' Namespace prefix | ||
222 | before listing any other user's mailboxes */ | ||
223 | free (ref); | ||
224 | return io_completion_response (command, RESP_NO, | ||
225 | "The requested item could not be found."); | ||
226 | } | ||
227 | |||
228 | status = mu_folder_create (&folder, cwd); | ||
229 | if (status) | ||
230 | { | ||
231 | free (ref); | ||
232 | free (cwd); | ||
233 | return io_completion_response (command, RESP_NO, | ||
234 | "The requested item could not be found."); | ||
235 | } | ||
236 | /* Force the right matcher */ | ||
237 | mu_folder_set_match (folder, mu_folder_imap_match); | ||
238 | |||
239 | memset (&refinfo, 0, sizeof refinfo); | ||
240 | |||
241 | refinfo.refptr = ref; | ||
242 | refinfo.reflen = strlen (ref); | ||
243 | refinfo.pfx = pfx; | ||
244 | |||
245 | mu_folder_get_url (folder, &url); | ||
246 | mu_url_sget_path (url, &dir); | ||
247 | refinfo.dirlen = strlen (dir); | ||
248 | |||
249 | /* The special name INBOX is included in the output from LIST, if | ||
250 | INBOX is supported by this server for this user and if the | ||
251 | uppercase string "INBOX" matches the interpreted reference and | ||
252 | mailbox name arguments with wildcards as described above. The | ||
253 | criteria for omitting INBOX is whether SELECT INBOX will return | ||
254 | failure; it is not relevant whether the user's real INBOX resides | ||
255 | on this or some other server. */ | ||
256 | |||
257 | if (!*ref && | ||
258 | (mu_imap_wildmatch (wcard, "INBOX", MU_HIERARCHY_DELIMITER) == 0 | ||
259 | || mu_imap_wildmatch (wcard, "inbox", MU_HIERARCHY_DELIMITER) == 0)) | ||
260 | io_untagged_response (RESP_NONE, "LIST (\\NoInferiors) NIL INBOX"); | ||
261 | 284 | ||
262 | mu_folder_enumerate (folder, NULL, wcard, 0, 0, NULL, | ||
263 | list_fun, &refinfo); | ||
264 | mu_folder_destroy (&folder); | ||
265 | free (refinfo.buf); | ||
266 | free (cwd); | 285 | free (cwd); |
267 | free (ref); | 286 | free (ref); |
268 | } | 287 | } |
269 | 288 | ||
270 | return io_completion_response (command, RESP_OK, "Completed"); | 289 | return io_completion_response (command, status, resp_text[status]); |
271 | } | 290 | } |
272 | 291 | ... | ... |
... | @@ -161,8 +161,16 @@ prefix_translate_name (struct namespace_prefix const *pfx, char const *name, | ... | @@ -161,8 +161,16 @@ prefix_translate_name (struct namespace_prefix const *pfx, char const *name, |
161 | size_t namelen, int url) | 161 | size_t namelen, int url) |
162 | { | 162 | { |
163 | size_t pfxlen = strlen (pfx->prefix); | 163 | size_t pfxlen = strlen (pfx->prefix); |
164 | 164 | int delim = 0; | |
165 | if (pfxlen <= namelen && memcmp (pfx->prefix, name, pfxlen) == 0) | 165 | |
166 | if (pfxlen && pfx->prefix[pfxlen-1] == pfx->delim) | ||
167 | { | ||
168 | pfxlen--; | ||
169 | delim = pfx->delim; | ||
170 | } | ||
171 | |||
172 | if ((pfxlen <= namelen && memcmp (pfx->prefix, name, pfxlen) == 0) | ||
173 | && (delim == 0 || (name[pfxlen] == delim || name[pfxlen] == 0))) | ||
166 | { | 174 | { |
167 | char *tmpl, *p; | 175 | char *tmpl, *p; |
168 | 176 | ... | ... |
-
Please register or sign in to post a comment