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 (&registrar_monitor);
91   if (registrar_list == NULL)
92     status = mu_list_create (&registrar_list);
93   *plist = registrar_list;
94   mu_monitor_unlock (&registrar_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 (&registrar_monitor);
105   if (registrar_list == NULL)
106     {
107       status = mu_list_create (&registrar_list);
108       if (status)
109 	return status;
110     }
111   status = mu_list_get_iterator (registrar_list, pitr);
112   mu_monitor_unlock (&registrar_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 (&registrar_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 (&registrar_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 (&registrar_monitor);
491   rc = mu_list_map (registrar_list, select_match, &mc, 1, &lst);
492   mu_monitor_unlock (&registrar_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