Commit 3f1703cb 3f1703cb95637b3ae0e319309cb6891e5b0d7434 by Sergey Poznyakoff

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.
1 parent 09105467
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
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 */
...@@ -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,27 +236,30 @@ imap4d_list (struct imap4d_session *session, ...@@ -169,27 +236,30 @@ 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)
186 } 247 {
248 char *p = wcard + strlen (pfx->prefix);
249 ref = mu_strdup (pfx->prefix);
250 memmove (wcard, p, strlen (p) + 1);
187 free (cwd); 251 free (cwd);
188 252 }
253 else
254 ref = mu_strdup (ref);
255 }
256 else
189 ref = mu_strdup (ref); 257 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, "%*");
261 if (wcard[i])
262 {
193 while (i > 0 && wcard[i - 1] != pfx->delim) 263 while (i > 0 && wcard[i - 1] != pfx->delim)
194 i--; 264 i--;
195 /* Append it to the reference */ 265 /* Append it to the reference */
...@@ -204,69 +274,18 @@ imap4d_list (struct imap4d_session *session, ...@@ -204,69 +274,18 @@ imap4d_list (struct imap4d_session *session,
204 274
205 wcard += i; 275 wcard += i;
206 } 276 }
207
208 cwd = namespace_translate_name (ref, 0, &pfx);
209 if (!cwd)
210 {
211 free (ref);
212 return io_completion_response (command, RESP_NO,
213 "The requested item could not be found.");
214 } 277 }
215 278
216 if (pfx->ns == NS_OTHER 279 cwd = namespace_translate_name (ref, 0, &pfx);
217 && strcmp (ref, pfx->prefix) == 0 280 if (cwd)
218 && strpbrk (wcard, "*%")) 281 status = list_ref (ref, wcard, cwd, pfx);
219 { 282 else
220 /* [A] server MAY return NO to such a LIST command, requiring that a 283 status = RESP_NO;
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 int delim = 0;
164 165
165 if (pfxlen <= namelen && memcmp (pfx->prefix, name, pfxlen) == 0) 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
......
...@@ -22,7 +22,7 @@ namespace personal { ...@@ -22,7 +22,7 @@ namespace personal {
22 prefix "" { 22 prefix "" {
23 directory "$HOMEDIR"; 23 directory "$HOMEDIR";
24 } 24 }
25 prefix "~" { 25 prefix "~/" {
26 directory "$HOMEDIR"; 26 directory "$HOMEDIR";
27 } 27 }
28 } 28 }
......