1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999-2021 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include "imap4d.h"
18 #include <mailutils/assoc.h>
19
20 struct namespace namespace[NS_MAX] = {
21 [NS_PERSONAL] = { "personal" },
22 [NS_OTHER] = { "other" },
23 [NS_SHARED] = { "shared" }
24 };
25
26 static mu_assoc_t prefixes;
27
28 struct namespace *
namespace_lookup(char const * name)29 namespace_lookup (char const *name)
30 {
31 size_t i;
32
33 for (i = 0; i < NS_MAX; i++)
34 if (strcmp (namespace[i].name, name) == 0)
35 {
36 if (!namespace[i].prefixes)
37 {
38 int rc = mu_list_create (&namespace[i].prefixes);
39 if (rc)
40 {
41 mu_diag_funcall (MU_DIAG_ERROR, "mu_list_create", NULL, rc);
42 abort ();
43 }
44 }
45 return &namespace[i];
46 }
47 return NULL;
48 }
49
50 static int
cmplen(const char * aname,const void * adata,const char * bname,const void * bdata,void * closure)51 cmplen (const char *aname, const void *adata,
52 const char *bname, const void *bdata,
53 void *closure)
54 {
55 return strlen (bname) - strlen (aname);
56 }
57
58 static void
trim_delim(char * str,int delim)59 trim_delim (char *str, int delim)
60 {
61 size_t len = strlen (str);
62 while (len && str[len-1] == '/')
63 len--;
64 str[len] = 0;
65 }
66
67 void
namespace_init(void)68 namespace_init (void)
69 {
70 int i;
71 int rc;
72 struct namespace_prefix *pfx;
73
74 if (mu_assoc_create (&prefixes, 0))
75 imap4d_bye (ERR_NO_MEM);
76 for (i = 0; i < NS_MAX; i++)
77 {
78 mu_iterator_t itr;
79
80 if (mu_list_is_empty (namespace[i].prefixes))
81 continue;
82
83 if (mu_list_get_iterator (namespace[i].prefixes, &itr))
84 imap4d_bye (ERR_NO_MEM);
85
86 for (mu_iterator_first (itr);
87 !mu_iterator_is_done (itr); mu_iterator_next (itr))
88 {
89 rc = mu_iterator_current (itr, (void **)&pfx);
90 if (rc)
91 {
92 mu_diag_funcall (MU_DIAG_ERROR, "mu_iterator_current", NULL, rc);
93 continue;
94 }
95 pfx->ns = i;
96
97 trim_delim (pfx->dir, '/');
98
99 if (!pfx->scheme)
100 mu_registrar_get_default_record (&pfx->record);
101
102 rc = mu_assoc_install (prefixes, pfx->prefix, pfx);
103 if (rc == MU_ERR_EXISTS)
104 {
105 mu_error (_("%s: namespace prefix appears twice"), pfx->prefix);
106 exit (EX_CONFIG);
107 }
108 else if (rc)
109 {
110 mu_error (_("%s: can't install prefix: %s"),
111 pfx->prefix, mu_strerror (rc));
112 exit (EX_CONFIG);
113 }
114 }
115 }
116
117 pfx = mu_assoc_get (prefixes, "");
118 if (pfx)
119 {
120 if (pfx->ns != NS_PERSONAL)
121 {
122 mu_error (_("empty prefix not allowed in the namespace %s"),
123 namespace[pfx->ns].name);
124 exit (EX_CONFIG);
125 }
126 }
127 else
128 {
129 struct namespace *priv;
130
131 pfx = mu_zalloc (sizeof (*pfx));
132 pfx->prefix = mu_strdup ("");
133 pfx->dir = mu_strdup ("$home");
134 pfx->delim = '/';
135 mu_registrar_get_default_record (&pfx->record);
136 priv = namespace_lookup ("personal");
137 mu_list_prepend (priv->prefixes, pfx);
138 rc = mu_assoc_install (prefixes, pfx->prefix, pfx);
139 if (rc)
140 {
141 mu_error (_("%s: can't install prefix: %s"),
142 pfx->prefix, mu_strerror (rc));
143 exit (EX_CONFIG);
144 }
145 }
146
147 mu_assoc_sort_r (prefixes, cmplen, NULL);
148 }
149
150 void
translate_delim(char * dst,char const * src,int dst_delim,int src_delim)151 translate_delim (char *dst, char const *src, int dst_delim, int src_delim)
152 {
153 do
154 *dst++ = *src == src_delim ? dst_delim : *src;
155 while (*src++);
156 }
157
158 static char *
prefix_translate_name(struct namespace_prefix const * pfx,char const * name,size_t namelen)159 prefix_translate_name (struct namespace_prefix const *pfx, char const *name,
160 size_t namelen)
161 {
162 size_t pfxlen = strlen (pfx->prefix);
163
164 if (pfxlen <= namelen && memcmp (pfx->prefix, name, pfxlen) == 0)
165 {
166 char *tmpl, *p;
167
168 name += pfxlen;
169
170 if (pfx->ns == NS_PERSONAL && pfxlen == 0 &&
171 mu_c_strcasecmp (name, "INBOX") == 0)
172 {
173 tmpl = mu_strdup (auth_data->mailbox);
174 return tmpl;//FIXME
175 }
176
177 tmpl = mu_alloc (namelen - pfxlen + strlen (pfx->dir) + 2);
178 p = tmpl;
179
180 p = mu_stpcpy (p, pfx->dir);
181 if (*name)
182 {
183 if (pfx->ns == NS_OTHER)
184 {
185 if (pfx->prefix[strlen (pfx->prefix) - 1] == pfx->delim)
186 ++name;
187
188 while (*name && *name != pfx->delim)
189 name++;
190 }
191 else if (*name != pfx->delim)
192 *p++ = '/';
193 translate_delim (p, name, '/', pfx->delim);
194 }
195
196 return tmpl;
197 }
198 return NULL;
199 }
200
201 static char *
translate_name(char const * name,struct namespace_prefix const ** return_pfx)202 translate_name (char const *name, struct namespace_prefix const **return_pfx)
203 {
204 mu_iterator_t itr;
205 int rc;
206 char *res = NULL;
207 size_t namelen = strlen (name);
208
209 rc = mu_assoc_get_iterator (prefixes, &itr);
210 if (rc)
211 {
212 mu_diag_funcall (MU_DIAG_ERROR, "mu_assoc_get_iterator", NULL, rc);
213 return NULL;
214 }
215 for (mu_iterator_first (itr);
216 !mu_iterator_is_done (itr); mu_iterator_next (itr))
217 {
218 struct namespace_prefix *pfx;
219
220 rc = mu_iterator_current (itr, (void **)&pfx);
221 if (rc)
222 {
223 mu_diag_funcall (MU_DIAG_ERROR, "mu_iterator_current", NULL, rc);
224 continue;
225 }
226
227 res = prefix_translate_name (pfx, name, namelen);
228 if (res)
229 {
230 if (return_pfx)
231 *return_pfx = pfx;
232 break;
233 }
234 }
235 mu_iterator_destroy (&itr);
236
237 return res;
238 }
239
240 static char *
extract_username(char const * name,struct namespace_prefix const * pfx)241 extract_username (char const *name, struct namespace_prefix const *pfx)
242 {
243 char const *p;
244 char *end;
245 char *user;
246 size_t len;
247
248 if (strlen (name) < strlen (pfx->prefix))
249 return NULL;
250 p = name + strlen (pfx->prefix);
251 end = strchr (p, pfx->delim);
252
253 if (end)
254 len = end - p;
255 else
256 len = strlen (p);
257
258 if (len == 0)
259 return mu_strdup (auth_data->name);
260
261 user = mu_alloc (len + 1);
262 memcpy (user, p, len);
263 user[len] = 0;
264 return user;
265 }
266
267 char *
namespace_translate_name(char const * name,struct namespace_prefix const ** return_pfx)268 namespace_translate_name (char const *name,
269 struct namespace_prefix const **return_pfx)
270 {
271 char *res = NULL;
272 struct namespace_prefix const *pfx;
273
274 res = translate_name (name, &pfx);
275
276 if (res)
277 {
278 mu_assoc_t assoc;
279 int rc;
280 char *dir;
281
282 rc = mu_assoc_create (&assoc, 0);
283 if (rc)
284 {
285 mu_diag_funcall (MU_DIAG_ERROR, "mu_assoc_create", NULL, rc);
286 free (res);
287 imap4d_bye (ERR_NO_MEM);
288 }
289
290 switch (pfx->ns)
291 {
292 case NS_PERSONAL:
293 mu_assoc_install (assoc, "user", auth_data->name);
294 mu_assoc_install (assoc, "home", real_homedir);
295 break;
296
297 case NS_OTHER:
298 {
299 struct mu_auth_data *adata;
300 char *user = extract_username (name, pfx);
301 mu_assoc_install (assoc, "user", user);
302 adata = mu_get_auth_by_name (user);
303 if (adata)
304 {
305 mu_assoc_install (assoc, "home", mu_strdup (adata->dir));
306 mu_auth_data_free (adata);
307 }
308 mu_assoc_set_destroy_item (assoc, mu_list_free_item);
309 }
310 break;
311
312 case NS_SHARED:
313 break;
314 }
315
316 rc = mu_str_expand (&dir, res, assoc);
317 free (res);
318 mu_assoc_destroy (&assoc);
319 if (rc)
320 {
321 if (rc == MU_ERR_FAILURE)
322 {
323 mu_error (_("cannot expand line `%s': %s"), res, dir);
324 free (dir);
325 }
326 else
327 {
328 mu_error (_("cannot expand line `%s': %s"), res,
329 mu_strerror (rc));
330 }
331 imap4d_bye (ERR_NO_MEM);
332 }
333
334 res = dir;
335 trim_delim (res, '/');
336 }
337 else if (mu_c_strcasecmp (name, "INBOX") == 0 && auth_data->change_uid)
338 {
339 res = mu_strdup (auth_data->mailbox);
340 pfx = mu_assoc_get (prefixes, "");
341 }
342
343 if (res && return_pfx)
344 *return_pfx = pfx;
345
346 return res;
347 }
348
349 char *
namespace_get_name(char const * name,mu_record_t * rec,int * mode)350 namespace_get_name (char const *name, mu_record_t *rec, int *mode)
351 {
352 struct namespace_prefix const *pfx;
353 char *path = namespace_translate_name (name, &pfx);
354 if (strcmp (name, pfx->prefix) == 0)
355 {
356 free (path);
357 return NULL;
358 }
359 if (rec)
360 *rec = pfx->record;
361 if (mode)
362 *mode = namespace[pfx->ns].mode;
363 return path;
364 }
365
366 char *
namespace_decode_delim(struct namespace_prefix const * pfx,char const * src)367 namespace_decode_delim (struct namespace_prefix const *pfx, char const *src)
368 {
369 char *dst = mu_alloc (strlen (src) + 1);
370 translate_delim (dst, src, '/', pfx->delim);
371 return dst;
372 }
373
374 char *
namespace_encode_delim(struct namespace_prefix const * pfx,char const * src)375 namespace_encode_delim (struct namespace_prefix const *pfx, char const *src)
376 {
377 char *dst = mu_alloc (strlen (src) + 1);
378 translate_delim (dst, src, pfx->delim, '/');
379 return dst;
380 }
381
382
383 static int
prefix_printer(void * item,void * data)384 prefix_printer(void *item, void *data)
385 {
386 struct namespace_prefix *pfx = item;
387 int *first = data;
388
389 if (*first)
390 *first = 0;
391 else
392 io_sendf (" ");
393 io_sendf ("(\"%s\" \"%c\")", pfx->prefix, pfx->delim);
394 return 0;
395 }
396
397 static void
print_namespace(int ns)398 print_namespace (int ns)
399 {
400 if (mu_list_is_empty (namespace[ns].prefixes))
401 io_sendf ("NIL");
402 else
403 {
404 int first = 1;
405 io_sendf ("(");
406 mu_list_foreach (namespace[ns].prefixes, prefix_printer, &first);
407 io_sendf (")");
408 }
409 }
410
411 /*
412 5. NAMESPACE Command
413
414 Arguments: none
415
416 Response: an untagged NAMESPACE response that contains the prefix
417 and hierarchy delimiter to the server's Personal
418 Namespace(s), Other Users' Namespace(s), and Shared
419 Namespace(s) that the server wishes to expose. The
420 response will contain a NIL for any namespace class
421 that is not available. Namespace_Response_Extensions
422 MAY be included in the response.
423 Namespace_Response_Extensions which are not on the IETF
424 standards track, MUST be prefixed with an "X-".
425 */
426
427 int
imap4d_namespace(struct imap4d_session * session,struct imap4d_command * command,imap4d_tokbuf_t tok)428 imap4d_namespace (struct imap4d_session *session,
429 struct imap4d_command *command, imap4d_tokbuf_t tok)
430 {
431 if (imap4d_tokbuf_argc (tok) != 2)
432 return io_completion_response (command, RESP_BAD, "Invalid arguments");
433
434 io_sendf ("* NAMESPACE ");
435
436 print_namespace (NS_PERSONAL);
437 io_sendf (" ");
438 print_namespace (NS_OTHER);
439 io_sendf (" ");
440 print_namespace (NS_SHARED);
441 io_sendf ("\n");
442
443 return io_completion_response (command, RESP_OK, "Completed");
444 }
445