1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999-2021 Free Software Foundation, Inc.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 3 of the License, or (at your option) any later version.
8
9 This library 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 GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General
15 Public License along with this library. If not, see
16 <http://www.gnu.org/licenses/>. */
17
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
21
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #ifdef HAVE_STRINGS_H
26 # include <strings.h>
27 #endif
28 #include <unistd.h>
29
30 #include <mailutils/iterator.h>
31 #include <mailutils/list.h>
32 #include <mailutils/monitor.h>
33 #include <mailutils/errno.h>
34 #include <mailutils/nls.h>
35 #include <mailutils/error.h>
36 #include <mailutils/url.h>
37 #include <mailutils/util.h>
38 #include <mailutils/sys/registrar.h>
39
40 /* NOTE: We will leak here since the monitor and the registrar will never
41 be released. That's ok we can live with this, it's only done once. */
42 static mu_list_t registrar_list;
43 struct mu_monitor registrar_monitor = MU_MONITOR_INITIALIZER;
44
45 static mu_record_t mu_default_record;
46
47 void
mu_registrar_set_default_record(mu_record_t record)48 mu_registrar_set_default_record (mu_record_t record)
49 {
50 mu_default_record = record;
51 }
52
53 int
mu_registrar_get_default_record(mu_record_t * prec)54 mu_registrar_get_default_record (mu_record_t *prec)
55 {
56 if (mu_default_record)
57 {
58 if (prec)
59 *prec = mu_default_record;
60 return 0;
61 }
62 return MU_ERR_NOENT;
63 }
64
65 int
mu_registrar_set_default_scheme(const char * scheme)66 mu_registrar_set_default_scheme (const char *scheme)
67 {
68 int status;
69 mu_record_t rec;
70
71 status = mu_registrar_lookup_scheme (scheme, &rec);
72 if (status == 0)
73 mu_registrar_set_default_record (rec);
74 return status;
75 }
76
77 const char *
mu_registrar_get_default_scheme(void)78 mu_registrar_get_default_scheme (void)
79 {
80 return mu_default_record ? mu_default_record->scheme : NULL;
81 }
82
83 static int
_registrar_get_list(mu_list_t * plist)84 _registrar_get_list (mu_list_t *plist)
85 {
86 int status = 0;
87
88 if (plist == NULL)
89 return MU_ERR_OUT_PTR_NULL;
90 mu_monitor_wrlock (®istrar_monitor);
91 if (registrar_list == NULL)
92 status = mu_list_create (®istrar_list);
93 *plist = registrar_list;
94 mu_monitor_unlock (®istrar_monitor);
95 return status;
96 }
97
98 int
mu_registrar_get_iterator(mu_iterator_t * pitr)99 mu_registrar_get_iterator (mu_iterator_t *pitr)
100 {
101 int status = 0;
102 if (pitr == NULL)
103 return MU_ERR_OUT_PTR_NULL;
104 mu_monitor_wrlock (®istrar_monitor);
105 if (registrar_list == NULL)
106 {
107 status = mu_list_create (®istrar_list);
108 if (status)
109 return status;
110 }
111 status = mu_list_get_iterator (registrar_list, pitr);
112 mu_monitor_unlock (®istrar_monitor);
113 return status;
114 }
115
116 int
mu_registrar_lookup_scheme(const char * scheme,mu_record_t * precord)117 mu_registrar_lookup_scheme (const char *scheme, mu_record_t *precord)
118 {
119 size_t len;
120 mu_iterator_t iterator;
121 int status = mu_registrar_get_iterator (&iterator);
122 if (status != 0)
123 return status;
124 status = MU_ERR_NOENT;
125 len = strcspn (scheme, ":");
126 for (mu_iterator_first (iterator); !mu_iterator_is_done (iterator);
127 mu_iterator_next (iterator))
128 {
129 mu_record_t record;
130 mu_iterator_current (iterator, (void **)&record);
131 if (strlen (record->scheme) == len
132 && memcmp (record->scheme, scheme, len) == 0)
133 {
134 if (precord)
135 *precord = record;
136 status = 0;
137 break;
138 }
139 }
140 mu_iterator_destroy (&iterator);
141 return status;
142 }
143
144 int
mu_registrar_lookup_url(mu_url_t url,int flags,mu_record_t * precord,int * pflags)145 mu_registrar_lookup_url (mu_url_t url, int flags,
146 mu_record_t *precord, int *pflags)
147 {
148 mu_iterator_t iterator;
149 mu_record_t last_record = NULL;
150 int last_flags = 0;
151 int status;
152
153 status = mu_registrar_get_iterator (&iterator);
154 if (status != 0)
155 return status;
156 status = MU_ERR_NOENT;
157 for (mu_iterator_first (iterator); !mu_iterator_is_done (iterator);
158 mu_iterator_next (iterator))
159 {
160 int rc;
161 mu_record_t record;
162 mu_iterator_current (iterator, (void **)&record);
163 if ((rc = mu_record_is_scheme (record, url, flags)))
164 {
165 if (rc == flags)
166 {
167 status = 0;
168 last_record = record;
169 last_flags = rc;
170 break;
171 }
172 else if (rc > last_flags)
173 {
174 status = 0;
175 last_record = record;
176 last_flags = rc;
177 }
178 }
179 }
180 mu_iterator_destroy (&iterator);
181
182 if (status
183 && !mu_is_proto (mu_url_to_string (url)) /* FIXME: This check is not
184 enough. */
185 && mu_default_record
186 && (mu_record_is_local (mu_default_record) == 0
187 || access (mu_url_to_string (url), F_OK)))
188 {
189 /* If the default record is local and the mailbox does not exist,
190 it possibly can be created if requested. */
191 status = 0;
192 last_record = mu_default_record;
193 last_flags = flags & MU_FOLDER_ATTRIBUTE_FILE; /* FIXME? */
194 }
195
196 if (status == 0)
197 {
198 if (precord)
199 *precord = last_record;
200 if (pflags)
201 *pflags = last_flags;
202 }
203
204 return status;
205 }
206
207 int
mu_registrar_lookup(const char * name,int flags,mu_record_t * precord,int * pflags)208 mu_registrar_lookup (const char *name, int flags,
209 mu_record_t *precord, int *pflags)
210 {
211 int rc;
212 mu_url_t url;
213
214 rc = mu_url_create (&url, name);
215 if (rc)
216 return rc;
217 rc = mu_registrar_lookup_url (url, flags, precord, pflags);
218 mu_url_destroy (&url);
219 return rc;
220 }
221
222 static int
_compare_prio(const void * item,const void * value)223 _compare_prio (const void *item, const void *value)
224 {
225 const mu_record_t a = (const mu_record_t) item;
226 const mu_record_t b = (const mu_record_t) value;
227 if (a->priority > b->priority)
228 return 0;
229 return -1;
230 }
231
232 int
mu_registrar_record(mu_record_t record)233 mu_registrar_record (mu_record_t record)
234 {
235 int status;
236 mu_list_t list;
237 mu_list_comparator_t comp;
238
239 if (!record)
240 return 0;
241 _registrar_get_list (&list);
242 comp = mu_list_set_comparator (list, _compare_prio);
243 status = mu_list_insert (list, record, record, 1);
244 if (status == MU_ERR_NOENT)
245 status = mu_list_append (list, record);
246 mu_list_set_comparator (list, comp);
247 return status;
248 }
249
250 int
mu_unregistrar_record(mu_record_t record)251 mu_unregistrar_record (mu_record_t record)
252 {
253 mu_list_t list;
254 _registrar_get_list (&list);
255 mu_list_remove (list, record);
256 return 0;
257 }
258
259 int
mu_record_is_scheme(mu_record_t record,mu_url_t url,int flags)260 mu_record_is_scheme (mu_record_t record, mu_url_t url, int flags)
261 {
262 if (record == NULL)
263 return 0;
264
265 /* Overload. */
266 if (record->_is_scheme)
267 return record->_is_scheme (record, url, flags);
268
269 if (mu_url_is_scheme (url, record->scheme))
270 return MU_FOLDER_ATTRIBUTE_ALL;
271
272 return 0;
273 }
274
275 int
mu_record_is_local(mu_record_t record)276 mu_record_is_local (mu_record_t record)
277 {
278 return record->flags & MU_RECORD_LOCAL;
279 }
280
281 int
mu_record_get_url(mu_record_t record,int (* (* _purl))(mu_url_t))282 mu_record_get_url (mu_record_t record, int (*(*_purl)) (mu_url_t))
283 {
284 if (record == NULL)
285 return EINVAL;
286 if (_purl == NULL)
287 return MU_ERR_OUT_PTR_NULL;
288 /* Overload. */
289 if (record->_get_url)
290 return record->_get_url (record, _purl);
291 *_purl = record->_url;
292 return 0;
293 }
294
295 int
mu_record_get_mailbox(mu_record_t record,int (* (* _pmailbox))(mu_mailbox_t))296 mu_record_get_mailbox (mu_record_t record, int (*(*_pmailbox)) (mu_mailbox_t))
297 {
298 if (record == NULL)
299 return EINVAL;
300 if (_pmailbox == NULL)
301 return MU_ERR_OUT_PTR_NULL;
302 /* Overload. */
303 if (record->_get_mailbox)
304 return record->_get_mailbox (record, _pmailbox);
305 *_pmailbox = record->_mailbox;
306 return 0;
307 }
308
309 int
mu_record_get_mailer(mu_record_t record,int (* (* _pmailer))(mu_mailer_t))310 mu_record_get_mailer (mu_record_t record, int (*(*_pmailer)) (mu_mailer_t))
311 {
312 if (record == NULL)
313 return EINVAL;
314 if (_pmailer == NULL)
315 return MU_ERR_OUT_PTR_NULL;
316 /* Overload. */
317 if (record->_get_mailer)
318 return record->_get_mailer (record, _pmailer);
319 *_pmailer = record->_mailer;
320 return 0;
321 }
322
323 int
mu_record_get_folder(mu_record_t record,int (* (* _pfolder))(mu_folder_t))324 mu_record_get_folder (mu_record_t record, int (*(*_pfolder)) (mu_folder_t))
325 {
326 if (record == NULL)
327 return EINVAL;
328 if (_pfolder == NULL)
329 return MU_ERR_OUT_PTR_NULL;
330 /* Overload. */
331 if (record->_get_folder)
332 return record->_get_folder (record, _pfolder);
333 *_pfolder = record->_folder;
334 return 0;
335 }
336
337 int
mu_record_list_p(mu_record_t record,const char * name,int flags)338 mu_record_list_p (mu_record_t record, const char *name, int flags)
339 {
340 return record == NULL
341 || !record->_list_p
342 || record->_list_p (record, name, flags);
343 }
344
345 int
mu_record_check_url(mu_record_t record,mu_url_t url,int * pmask)346 mu_record_check_url (mu_record_t record, mu_url_t url, int *pmask)
347 {
348 int mask;
349 int flags;
350 int rc;
351
352 if (!record || !url)
353 return EINVAL;
354
355 rc = mu_url_get_flags (url, &flags);
356 if (rc)
357 return rc;
358
359 mask = flags & record->url_must_have;
360 if (mask != record->url_must_have)
361 {
362 if (pmask)
363 *pmask = record->url_must_have & ~mask;
364 return MU_ERR_URL_MISS_PARTS;
365 }
366 mask = flags & ~(record->url_may_have | record->url_must_have);
367 if (mask)
368 {
369 if (pmask)
370 *pmask = mask;
371 return MU_ERR_URL_EXTRA_PARTS;
372 }
373 return 0;
374 }
375
376 /* Test if URL corresponds to a local record.
377 Return:
378 0 - OK, the result is stored in *pres;
379 MU_ERR_NOENT - don't know: there's no matching record;
380 EINVAL - some of the arguments is not valid;
381 other - URL lookup failed.
382 */
383 int
mu_registrar_test_local_url(mu_url_t url,int * pres)384 mu_registrar_test_local_url (mu_url_t url, int *pres)
385 {
386 int rc;
387 mu_record_t rec;
388
389 if (!url || !pres)
390 return EINVAL;
391 rc = mu_registrar_lookup_url (url, MU_FOLDER_ATTRIBUTE_ALL, &rec, NULL);
392 if (rc)
393 return rc;
394 *pres = mu_record_is_local (rec);
395 return 0;
396 }
397
398 struct listable_closure
399 {
400 char const *name;
401 int flags;
402 };
403
404 static int
record_listable(void * item,void * data)405 record_listable (void *item, void *data)
406 {
407 mu_record_t record = item;
408 struct listable_closure *cls = data;
409 return !mu_record_list_p (record, cls->name, cls->flags);
410 }
411
412 int
mu_registrar_list_p(mu_list_t rlist,char const * name,int flags)413 mu_registrar_list_p (mu_list_t rlist, char const *name, int flags)
414 {
415 struct listable_closure cls = { name, flags };
416 if (!rlist)
417 rlist = registrar_list;
418 return !mu_list_foreach (rlist, record_listable, &cls);
419 }
420
421 /* Apply flt to each record in the registry and remove those, for which it
422 returns non-zero. */
423 int
mu_registrar_apply_filter(int (* flt)(mu_record_t,void *),void * data)424 mu_registrar_apply_filter (int (*flt) (mu_record_t, void *), void *data)
425 {
426 mu_iterator_t iterator;
427
428 int status = mu_registrar_get_iterator (&iterator);
429 if (status != 0)
430 return status;
431 mu_monitor_wrlock (®istrar_monitor);
432 for (mu_iterator_first (iterator); !mu_iterator_is_done (iterator);
433 mu_iterator_next (iterator))
434 {
435 mu_record_t record;
436 mu_iterator_current (iterator, (void **)&record);
437 if (flt (record, data))
438 mu_list_remove (registrar_list, record);
439 }
440 mu_iterator_destroy (&iterator);
441 mu_monitor_unlock (®istrar_monitor);
442 return 0;
443 }
444
445 struct match_closure
446 {
447 mu_url_t url;
448 int flags;
449 int err;
450 };
451
452 static int
select_match(void ** itmv,size_t itmc,void * call_data)453 select_match (void **itmv, size_t itmc, void *call_data)
454 {
455 struct match_closure *mc = call_data;
456 int rc = mu_record_is_scheme (itmv[0], mc->url, mc->flags);
457 if (rc)
458 {
459 struct mu_record_match *match = malloc (sizeof (*match));
460 if (!match)
461 {
462 mc->err = errno;
463 return MU_LIST_MAP_STOP;
464 }
465 match->record = itmv[0];
466 match->flags = rc;
467 itmv[0] = match;
468 return MU_LIST_MAP_OK;
469 }
470 return MU_LIST_MAP_SKIP;
471 }
472
473 /* Select records matching pathname NAME with given FLAGS
474 (MU_FOLDER_ATTRIBUTE_* bitmask). On success, store each
475 match as a pointer to struct mu_record_match in *RET.
476 */
477 int
mu_registrar_match_records(char const * name,int flags,mu_list_t * ret)478 mu_registrar_match_records (char const *name, int flags, mu_list_t *ret)
479 {
480 int rc;
481 struct match_closure mc;
482 mu_list_t lst;
483
484 rc = mu_url_create (&mc.url, name);
485 if (rc)
486 return rc;
487 mc.flags = flags;
488 mc.err = 0;
489
490 mu_monitor_wrlock (®istrar_monitor);
491 rc = mu_list_map (registrar_list, select_match, &mc, 1, &lst);
492 mu_monitor_unlock (®istrar_monitor);
493 mu_url_destroy (&mc.url);
494 if (rc == 0)
495 {
496 mu_list_set_destroy_item (lst, mu_list_free_item);
497 if (mc.err)
498 {
499 mu_list_destroy (&lst);
500 rc = mc.err;
501 }
502 }
503 if (rc == 0)
504 *ret = lst;
505 return rc;
506 }
507