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 <errno.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <fnmatch.h>
26
27 #include <mailutils/auth.h>
28 #include <mailutils/debug.h>
29 #include <mailutils/iterator.h>
30 #include <mailutils/list.h>
31 #include <mailutils/monitor.h>
32 #include <mailutils/observer.h>
33 #include <mailutils/registrar.h>
34 #include <mailutils/url.h>
35 #include <mailutils/errno.h>
36 #include <mailutils/property.h>
37 #include <mailutils/mailbox.h>
38 #include <mailutils/imaputil.h>
39 #include <mailutils/util.h>
40 #include <mailutils/sys/folder.h>
41
42 int
mu_folder_glob_match(const char * name,void * pattern,int flags)43 mu_folder_glob_match (const char *name, void *pattern, int flags)
44 {
45 return fnmatch (pattern, name[0] == '/' ? name + 1 : name, 0);
46 }
47
48 int
mu_folder_imap_match(const char * name,void * pattern,int flags)49 mu_folder_imap_match (const char *name, void *pattern, int flags)
50 {
51 return mu_imap_wildmatch (pattern, name, '/');
52 }
53
54 int
mu_folder_create_from_record(mu_folder_t * pfolder,mu_url_t url,mu_record_t record)55 mu_folder_create_from_record (mu_folder_t *pfolder, mu_url_t url,
56 mu_record_t record)
57 {
58 if (!pfolder)
59 return MU_ERR_OUT_PTR_NULL;
60
61 if (record ||
62 /* Look in the registrar list(iterator), for a possible concrete mailbox
63 implementation that could match the URL. */
64 mu_registrar_lookup_url (url, MU_FOLDER_ATTRIBUTE_DIRECTORY, &record,
65 NULL) == 0)
66 {
67 int (*f_init) (mu_folder_t) = NULL;
68
69 mu_record_get_folder (record, &f_init);
70 if (f_init)
71 {
72 int status, mask;
73 mu_folder_t folder;
74 int (*u_init) (mu_url_t) = NULL;
75
76 status = mu_record_check_url (record, url, &mask);
77 if (status)
78 /* FIXME: mask would provide more info */
79 return status;
80
81 mu_record_get_url (record, &u_init);
82 if (u_init)
83 {
84 status = u_init (url);
85 if (status)
86 return status;
87 }
88
89 /* Create a new folder. */
90
91 /* Allocate memory for the folder. */
92 folder = calloc (1, sizeof (*folder));
93 if (folder != NULL)
94 {
95 folder->url = url;
96 folder->is_local = record->flags & MU_RECORD_LOCAL;
97 /* Initialize the internal foilder lock, now so the
98 concrete folder could use it. */
99 status = mu_monitor_create (&folder->monitor, 0, folder);
100 if (status == 0)
101 {
102 /* Create the concrete folder type. */
103 status = f_init (folder);
104 if (status == 0)
105 {
106 if (!folder->_match)
107 folder->_match = mu_folder_imap_match;
108 *pfolder = folder;
109 folder->ref++;
110 }
111 }
112 /* Something went wrong, destroy the object. */
113 if (status)
114 {
115 if (folder->monitor)
116 mu_monitor_destroy (&folder->monitor, folder);
117 free (folder);
118 }
119 }
120 return status;
121 }
122 }
123
124 return MU_ERR_NOENT;
125 }
126
127 int
mu_folder_create(mu_folder_t * pfolder,const char * name)128 mu_folder_create (mu_folder_t *pfolder, const char *name)
129 {
130 int rc;
131 mu_url_t url;
132
133 rc = mu_url_create (&url, name);
134 if (rc)
135 return rc;
136 rc = mu_folder_create_from_record (pfolder, url, NULL);
137 if (rc)
138 mu_url_destroy (&url);
139 return rc;
140 }
141
142 int
mu_folder_attach_ticket(mu_folder_t folder)143 mu_folder_attach_ticket (mu_folder_t folder)
144 {
145 mu_authority_t auth = NULL;
146 int rc = MU_ERR_NOENT;
147
148 if (mu_folder_get_authority (folder, &auth) == 0 && auth)
149 {
150 char *filename = mu_tilde_expansion (mu_ticket_file,
151 MU_HIERARCHY_DELIMITER, NULL);
152 mu_wicket_t wicket;
153
154 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1,
155 ("Reading user ticket file %s", filename));
156 if ((rc = mu_file_wicket_create (&wicket, filename)) == 0)
157 {
158 mu_ticket_t ticket;
159
160 if ((rc = mu_wicket_get_ticket (wicket, NULL, &ticket)) == 0)
161 {
162 rc = mu_authority_set_ticket (auth, ticket);
163 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1,
164 ("Retrieved and set ticket: %d", rc));
165 }
166 else
167 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
168 ("Error retrieving ticket: %s\n",
169 mu_strerror (rc)));
170 mu_wicket_destroy (&wicket);
171 }
172 else
173 mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
174 ("Error creating wicket: %s\n", mu_strerror (rc)));
175 free (filename);
176 }
177 return rc;
178 }
179
180 int
mu_folder_is_local(mu_folder_t folder)181 mu_folder_is_local (mu_folder_t folder)
182 {
183 if (!folder)
184 return -1;
185 return folder->is_local;
186 }
187
188 /* The folder is destroy if it is the last reference. */
189 void
mu_folder_destroy(mu_folder_t * pfolder)190 mu_folder_destroy (mu_folder_t *pfolder)
191 {
192 if (pfolder && *pfolder)
193 {
194 mu_folder_t folder = *pfolder;
195 int destroy_lock = 0;
196 mu_monitor_t monitor = folder->monitor;
197
198 mu_monitor_wrlock (monitor);
199
200 folder->ref--;
201
202 if (folder->ref <= 0)
203 {
204 mu_monitor_unlock (monitor);
205 destroy_lock = 1;
206 /* Notify the observers. */
207 if (folder->observable)
208 {
209 mu_observable_notify (folder->observable, MU_EVT_FOLDER_DESTROY,
210 folder);
211 mu_observable_destroy (&folder->observable, folder);
212 }
213 if (folder->_destroy)
214 folder->_destroy (folder);
215 mu_monitor_wrlock (monitor);
216 if (folder->authority)
217 mu_authority_destroy (&folder->authority, folder);
218 if (folder->url)
219 mu_url_destroy (&folder->url);
220 if (folder->property)
221 mu_property_destroy (&folder->property);
222 free (folder);
223 }
224 mu_monitor_unlock (monitor);
225 if (destroy_lock)
226 mu_monitor_destroy (&monitor, folder);
227 *pfolder = NULL;
228 }
229 }
230
231 int
mu_folder_get_property(mu_folder_t folder,mu_property_t * prop)232 mu_folder_get_property (mu_folder_t folder, mu_property_t *prop)
233 {
234 if (folder == NULL)
235 return EINVAL;
236 if (prop == NULL)
237 return MU_ERR_OUT_PTR_NULL;
238
239 if (folder->property == NULL)
240 {
241 int status;
242
243 if (folder->_get_property)
244 status = folder->_get_property (folder, &folder->property);
245 else
246 status = mu_property_create_init (&folder->property,
247 mu_assoc_property_init, NULL);
248 if (status != 0)
249 return status;
250 }
251 *prop = folder->property;
252 return 0;
253 }
254
255
256 /* Cover functions. */
257 int
mu_folder_open(mu_folder_t folder,int flags)258 mu_folder_open (mu_folder_t folder, int flags)
259 {
260 if (folder == NULL)
261 return EINVAL;
262 if (folder->_open == NULL)
263 return ENOSYS;
264 return folder->_open (folder, flags);
265 }
266
267 int
mu_folder_close(mu_folder_t folder)268 mu_folder_close (mu_folder_t folder)
269 {
270 if (folder == NULL)
271 return EINVAL;
272 if (folder->_close == NULL)
273 return ENOSYS;
274 return folder->_close (folder);
275 }
276
277 int
mu_folder_set_authority(mu_folder_t folder,mu_authority_t authority)278 mu_folder_set_authority (mu_folder_t folder, mu_authority_t authority)
279 {
280 if (folder == NULL)
281 return EINVAL;
282 if (folder->authority)
283 mu_authority_destroy (&folder->authority, folder);
284 folder->authority = authority;
285 return 0;
286 }
287
288 int
mu_folder_get_authority(mu_folder_t folder,mu_authority_t * pauthority)289 mu_folder_get_authority (mu_folder_t folder, mu_authority_t *pauthority)
290 {
291 if (folder == NULL)
292 return EINVAL;
293 if (pauthority == NULL)
294 return MU_ERR_OUT_PTR_NULL;
295 *pauthority = folder->authority;
296 return 0;
297 }
298
299 int
mu_folder_get_observable(mu_folder_t folder,mu_observable_t * pobservable)300 mu_folder_get_observable (mu_folder_t folder, mu_observable_t *pobservable)
301 {
302 if (folder == NULL)
303 return EINVAL;
304 if (pobservable == NULL)
305 return MU_ERR_OUT_PTR_NULL;
306
307 if (folder->observable == NULL)
308 {
309 int status = mu_observable_create (&folder->observable, folder);
310 if (status != 0)
311 return status;
312 }
313 *pobservable = folder->observable;
314 return 0;
315 }
316
317 int
mu_folder_set_match(mu_folder_t folder,mu_folder_match_fp pmatch)318 mu_folder_set_match (mu_folder_t folder, mu_folder_match_fp pmatch)
319 {
320 if (folder == NULL)
321 return EINVAL;
322 folder->_match = pmatch;
323 return 0;
324 }
325
326 int
mu_folder_get_match(mu_folder_t folder,mu_folder_match_fp * pmatch)327 mu_folder_get_match (mu_folder_t folder, mu_folder_match_fp *pmatch)
328 {
329 if (folder == NULL)
330 return EINVAL;
331 if (pmatch == NULL)
332 return MU_ERR_OUT_PTR_NULL;
333 *pmatch = folder->_match;
334 return 0;
335 }
336
337 void
mu_list_response_free(void * data)338 mu_list_response_free (void *data)
339 {
340 struct mu_list_response *f = data;
341 free (f->name);
342 free (f);
343 }
344
345 int
mu_folder_scan(mu_folder_t folder,struct mu_folder_scanner * scn)346 mu_folder_scan (mu_folder_t folder, struct mu_folder_scanner *scn)
347 {
348 if (!folder || !scn)
349 return EINVAL;
350 if (folder->_list == NULL)
351 return ENOSYS;
352 if (scn->result)
353 mu_list_set_destroy_item (scn->result, mu_list_response_free);
354 return folder->_list (folder, scn);
355 }
356
357 int
mu_folder_list(mu_folder_t folder,const char * dirname,void * pattern,size_t max_depth,mu_list_t * pflist)358 mu_folder_list (mu_folder_t folder, const char *dirname, void *pattern,
359 size_t max_depth,
360 mu_list_t *pflist)
361 {
362 return mu_folder_enumerate (folder, dirname, pattern,
363 MU_FOLDER_ATTRIBUTE_ALL, max_depth,
364 pflist, NULL, NULL);
365 }
366
367 int
mu_folder_enumerate(mu_folder_t folder,const char * name,void * pattern,int flags,size_t max_depth,mu_list_t * pflist,mu_folder_enumerate_fp enumfun,void * enumdata)368 mu_folder_enumerate (mu_folder_t folder, const char *name,
369 void *pattern, int flags,
370 size_t max_depth,
371 mu_list_t *pflist,
372 mu_folder_enumerate_fp enumfun, void *enumdata)
373 {
374 int status;
375 if (folder == NULL || (!pflist && !enumfun))
376 return EINVAL;
377 else if (folder->_list == NULL)
378 return ENOSYS;
379 else
380 {
381 struct mu_folder_scanner scn;
382
383 scn.refname = name;
384 scn.pattern = pattern;
385 scn.match_flags = flags;
386 scn.max_depth = max_depth;
387 scn.enumfun = enumfun;
388 scn.enumdata = enumdata;
389 scn.records = NULL;
390 if (pflist)
391 {
392 status = mu_list_create (&scn.result);
393 if (status)
394 return status;
395 mu_list_set_destroy_item (scn.result, mu_list_response_free);
396 }
397 status = mu_folder_scan (folder, &scn);
398 if (status == 0)
399 {
400 if (pflist)
401 *pflist = scn.result;
402 }
403 else
404 mu_list_destroy (&scn.result);
405 }
406 return status;
407 }
408
409 int
mu_folder_lsub(mu_folder_t folder,const char * dirname,const char * basename,mu_list_t * pflist)410 mu_folder_lsub (mu_folder_t folder, const char *dirname, const char *basename,
411 mu_list_t *pflist)
412 {
413 int status;
414
415 if (folder == NULL)
416 return EINVAL;
417 else if (folder->_lsub == NULL)
418 return ENOSYS;
419 else
420 {
421 status = mu_list_create (pflist);
422 if (status)
423 return status;
424 mu_list_set_destroy_item (*pflist, mu_list_response_free);
425 status = folder->_lsub (folder, dirname, basename, *pflist);
426 }
427 return status;
428 }
429
430 int
mu_folder_subscribe(mu_folder_t folder,const char * name)431 mu_folder_subscribe (mu_folder_t folder, const char *name)
432 {
433 if (folder == NULL)
434 return EINVAL;
435 if (folder->_subscribe == NULL)
436 return ENOSYS;
437 return folder->_subscribe (folder, name);
438 }
439
440 int
mu_folder_unsubscribe(mu_folder_t folder,const char * name)441 mu_folder_unsubscribe (mu_folder_t folder, const char *name)
442 {
443 if (folder == NULL)
444 return EINVAL;
445 if (folder->_unsubscribe == NULL)
446 return ENOSYS;
447 return folder->_unsubscribe (folder, name);
448 }
449
450 int
mu_folder_delete(mu_folder_t folder,const char * name)451 mu_folder_delete (mu_folder_t folder, const char *name)
452 {
453 int rc;
454
455 if (folder == NULL)
456 return EINVAL;
457 if (folder->_delete)
458 rc = folder->_delete (folder, name);
459 else
460 {
461 /* If there is no folder-specific _delete method, then try to create the
462 mailbox and call mailbox delete (remove) method. This is necessary
463 because certain types of mailboxes share a common folder (e.g. mbox,
464 maildir and mh all use filesystem folder), but have a different
465 internal structure. Supplying mu_folder_t with knowledge about
466 mailbox internals will harm separation of concerns. On the other
467 hand, removing something without looking into it may well yield
468 undesired results. For example, a MH mailbox can hold another
469 mailboxes, i.e. be a folder itself. Removing it blindly would
470 result in removing these mailboxes as well, which is clearly not
471 intended.
472
473 To solve this, both folder and mailbox delete methods are tightly
474 paired, but without looking into each-others internal mechanisms. */
475 mu_mailbox_t mbox;
476 rc = mu_mailbox_create_at (&mbox, folder, name);
477 if (rc == 0)
478 {
479 rc = mu_mailbox_remove (mbox);
480 mu_mailbox_destroy (&mbox);
481 }
482 }
483 return rc;
484 }
485
486 int
mu_folder_rename(mu_folder_t folder,const char * oldname,const char * newname)487 mu_folder_rename (mu_folder_t folder, const char *oldname, const char *newname)
488 {
489 if (folder == NULL)
490 return EINVAL;
491 if (folder->_rename == NULL)
492 return ENOSYS;
493 return folder->_rename (folder, oldname, newname);
494 }
495
496 int
mu_folder_get_url(mu_folder_t folder,mu_url_t * purl)497 mu_folder_get_url (mu_folder_t folder, mu_url_t *purl)
498 {
499 if (folder == NULL)
500 return EINVAL;
501 if (purl == NULL)
502 return MU_ERR_OUT_PTR_NULL;
503 *purl = folder->url;
504 return 0;
505 }
506
507