diff options
Diffstat (limited to 'libmailutils/folder.c')
-rw-r--r-- | libmailutils/folder.c | 511 |
1 files changed, 511 insertions, 0 deletions
diff --git a/libmailutils/folder.c b/libmailutils/folder.c new file mode 100644 index 000000000..3ce430a15 --- /dev/null +++ b/libmailutils/folder.c @@ -0,0 +1,511 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 1999, 2000, 2001, 2004, 2005, 2006, 2007, 2008, 2009, + 2010 Free Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <fnmatch.h> + +#include <mailutils/auth.h> +#include <mailutils/debug.h> +#include <mailutils/iterator.h> +#include <mailutils/list.h> +#include <mailutils/monitor.h> +#include <mailutils/observer.h> +#include <mailutils/registrar.h> +#include <mailutils/stream.h> +#include <mailutils/url.h> +#include <mailutils/errno.h> + +#include <mailutils/sys/folder.h> + +/* Internal folder list. */ +static mu_list_t known_folder_list; +static int is_known_folder (mu_url_t, mu_folder_t *); + +/* Static folder lock. */ +static struct mu_monitor folder_lock = MU_MONITOR_INITIALIZER; + +int +mu_folder_match (const char *name, void *pattern, int flags) +{ + return fnmatch (pattern, name[0] == '/' ? name + 1 : name, flags); +} + +/* A folder could be remote (IMAP), or local(a spool directory) like $HOME/Mail + etc .. We maintain a list of known folders to avoid creating multiple + folders for the same URL. So, when mu_folder_create is called we check if + we already have a folder for that URL and return it, otherwise we create a + new one. Downsides: the scheme to detect the same URL is very weak, and + there could be cases where you'll want a different folder for the same URL, + there is not easy way to do this. */ +int +mu_folder_create_from_record (mu_folder_t *pfolder, mu_url_t url, + mu_record_t record) +{ + if (!pfolder) + return MU_ERR_OUT_PTR_NULL; + + if (record || + /* Look in the registrar list(iterator), for a possible concrete mailbox + implementation that could match the URL. */ + mu_registrar_lookup_url (url, MU_FOLDER_ATTRIBUTE_DIRECTORY, &record, + NULL) == 0) + { + int (*f_init) (mu_folder_t) = NULL; + + mu_record_get_folder (record, &f_init); + if (f_init) + { + int status; + mu_folder_t folder; + int (*u_init) (mu_url_t) = NULL; + + mu_record_get_url (record, &u_init); + if (u_init) + { + status = u_init (url); + if (status) + return status; + } + + mu_monitor_wrlock (&folder_lock); + + /* Check if we already have the same URL folder. */ + if (is_known_folder (url, &folder)) + { + folder->ref++; + *pfolder = folder; + mu_url_destroy (&url); /* FIXME: Hmm */ + mu_monitor_unlock (&folder_lock); + return 0; + } + else + mu_monitor_unlock (&folder_lock); + + /* Create a new folder. */ + + /* Allocate memory for the folder. */ + folder = calloc (1, sizeof (*folder)); + if (folder != NULL) + { + folder->url = url; + /* Initialize the internal foilder lock, now so the + concrete folder could use it. */ + status = mu_monitor_create (&folder->monitor, 0, folder); + if (status == 0) + { + /* Create the concrete folder type. */ + status = f_init (folder); + if (status == 0) + { + if (!folder->_match) + folder->_match = mu_folder_match; + *pfolder = folder; + folder->ref++; + /* Put on the internal list of known folders. */ + if (known_folder_list == NULL) + mu_list_create (&known_folder_list); + mu_list_append (known_folder_list, folder); + } + } + /* Something went wrong, destroy the object. */ + if (status) + { + if (folder->monitor) + mu_monitor_destroy (&folder->monitor, folder); + free (folder); + } + } + return status; + } + } + + return MU_ERR_NOENT; +} + +int +mu_folder_create (mu_folder_t *pfolder, const char *name) +{ + int rc; + mu_url_t url; + + rc = mu_url_create (&url, name); + if (rc) + return rc; + rc = mu_url_parse (url); + if (rc == 0) + rc = mu_folder_create_from_record (pfolder, url, NULL); + if (rc) + mu_url_destroy (&url); + return rc; +} + +/* The folder is destroy if it is the last reference. */ +void +mu_folder_destroy (mu_folder_t *pfolder) +{ + if (pfolder && *pfolder) + { + mu_folder_t folder = *pfolder; + int destroy_lock = 0; + mu_monitor_t monitor = folder->monitor; + + mu_monitor_wrlock (monitor); + + /* Check if this the last reference for this folder. If yes removed + it from the list. */ + mu_monitor_wrlock (&folder_lock); + folder->ref--; + /* Remove the folder from the list of known folder. */ + if (folder->ref <= 0) + mu_list_remove (known_folder_list, folder); + /* If the list is empty we can safely remove it. */ + if (mu_list_is_empty (known_folder_list)) + mu_list_destroy (&known_folder_list); + + mu_monitor_unlock (&folder_lock); + + if (folder->ref <= 0) + { + mu_monitor_unlock (monitor); + destroy_lock = 1; + /* Notify the observers. */ + if (folder->observable) + { + mu_observable_notify (folder->observable, MU_EVT_FOLDER_DESTROY, + folder); + mu_observable_destroy (&folder->observable, folder); + } + if (folder->_destroy) + folder->_destroy (folder); + mu_monitor_wrlock (monitor); + if (folder->authority) + mu_authority_destroy (&folder->authority, folder); + if (folder->stream) + mu_stream_destroy (&folder->stream); + if (folder->url) + mu_url_destroy (&folder->url); + free (folder); + } + mu_monitor_unlock (monitor); + if (destroy_lock) + mu_monitor_destroy (&monitor, folder); + *pfolder = NULL; + } +} + +/* Cover functions. */ +int +mu_folder_open (mu_folder_t folder, int flags) +{ + if (folder == NULL || folder->_open == NULL) + return ENOSYS; + return folder->_open (folder, flags); +} + +int +mu_folder_close (mu_folder_t folder) +{ + if (folder == NULL || folder->_close == NULL) + return ENOSYS; + return folder->_close (folder); +} + +int +mu_folder_set_stream (mu_folder_t folder, mu_stream_t stream) +{ + if (folder == NULL) + return EINVAL; + if (folder->stream) + mu_stream_destroy (&folder->stream); + folder->stream = stream; + return 0; +} + +int +mu_folder_get_stream (mu_folder_t folder, mu_stream_t *pstream) +{ + /* FIXME: Deprecation warning */ + if (folder == NULL) + return EINVAL; + if (pstream == NULL) + return MU_ERR_OUT_PTR_NULL; + *pstream = folder->stream; + return 0; +} + +int +mu_folder_get_streamref (mu_folder_t folder, mu_stream_t *pstream) +{ + if (folder == NULL) + return EINVAL; + if (pstream == NULL) + return MU_ERR_OUT_PTR_NULL; + return mu_streamref_create (pstream, folder->stream); +} + +int +mu_folder_set_authority (mu_folder_t folder, mu_authority_t authority) +{ + if (folder == NULL) + return EINVAL; + if (folder->authority) + mu_authority_destroy (&folder->authority, folder); + folder->authority = authority; + return 0; +} + +int +mu_folder_get_authority (mu_folder_t folder, mu_authority_t *pauthority) +{ + if (folder == NULL) + return EINVAL; + if (pauthority == NULL) + return MU_ERR_OUT_PTR_NULL; + *pauthority = folder->authority; + return 0; +} + +int +mu_folder_get_observable (mu_folder_t folder, mu_observable_t *pobservable) +{ + if (folder == NULL) + return EINVAL; + if (pobservable == NULL) + return MU_ERR_OUT_PTR_NULL; + + if (folder->observable == NULL) + { + int status = mu_observable_create (&folder->observable, folder); + if (status != 0) + return status; + } + *pobservable = folder->observable; + return 0; +} + +int +mu_folder_set_match (mu_folder_t folder, mu_folder_match_fp pmatch) +{ + if (folder == NULL) + return EINVAL; + folder->_match = pmatch; + return 0; +} + +int +mu_folder_get_match (mu_folder_t folder, mu_folder_match_fp *pmatch) +{ + if (folder == NULL) + return EINVAL; + if (pmatch == NULL) + return MU_ERR_OUT_PTR_NULL; + *pmatch = folder->_match; + return 0; +} + +int +mu_folder_has_debug (mu_folder_t folder) +{ + if (folder == NULL) + return 0; + + return folder->debug ? 1 : 0; +} + +int +mu_folder_set_debug (mu_folder_t folder, mu_debug_t debug) +{ + if (folder == NULL) + return EINVAL; + if (folder->debug) + mu_debug_destroy (&folder->debug, folder); + folder->debug = debug; + return 0; +} + +int +mu_folder_get_debug (mu_folder_t folder, mu_debug_t *pdebug) +{ + if (folder == NULL) + return EINVAL; + if (pdebug == NULL) + return MU_ERR_OUT_PTR_NULL; + if (folder->debug == NULL) + { + int status = mu_debug_create (&folder->debug, folder); + if (status != 0) + return status; + } + *pdebug = folder->debug; + return 0; +} + +void +mu_list_response_free (void *data) +{ + struct mu_list_response *f = data; + free (f->name); + free (f); +} + +int +mu_folder_list (mu_folder_t folder, const char *dirname, void *pattern, + size_t max_level, + mu_list_t *pflist) +{ + return mu_folder_enumerate (folder, dirname, pattern, 0, max_level, + pflist, NULL, NULL); +} + +int +mu_folder_enumerate (mu_folder_t folder, const char *name, + void *pattern, int flags, + size_t max_level, + mu_list_t *pflist, + mu_folder_enumerate_fp enumfun, void *enumdata) +{ + int status; + if (folder == NULL || folder->_list == NULL) + return EINVAL; + else + { + mu_list_t list = NULL; + + if (pflist) + { + status = mu_list_create (&list); + if (status) + return status; + *pflist = list; + mu_list_set_destroy_item (list, mu_list_response_free); + } + else if (!enumfun) + return EINVAL; + + status = folder->_list (folder, name, pattern, flags, max_level, + list, enumfun, enumdata); + if (status) + mu_list_destroy (pflist); + } + return status; +} + +int +mu_folder_lsub (mu_folder_t folder, const char *dirname, const char *basename, + mu_list_t *pflist) +{ + int status; + + if (folder == NULL || folder->_lsub == NULL) + return ENOSYS; + else + { + status = mu_list_create (pflist); + if (status) + return status; + mu_list_set_destroy_item (*pflist, mu_list_response_free); + status = folder->_lsub (folder, dirname, basename, *pflist); + } + return status; +} + +int +mu_folder_subscribe (mu_folder_t folder, const char *name) +{ + if (folder == NULL || folder->_subscribe == NULL) + return EINVAL; + return folder->_subscribe (folder, name); +} + +int +mu_folder_unsubscribe (mu_folder_t folder, const char *name) +{ + if (folder == NULL || folder->_unsubscribe == NULL) + return EINVAL; + return folder->_unsubscribe (folder, name); +} + +int +mu_folder_delete (mu_folder_t folder, const char *name) +{ + if (folder == NULL || folder->_delete == NULL) + return ENOSYS; + return folder->_delete (folder, name); +} + +int +mu_folder_rename (mu_folder_t folder, const char *oldname, const char *newname) +{ + if (folder == NULL || folder->_rename == NULL) + return ENOSYS; + return folder->_rename (folder, oldname, newname); +} + +int +mu_folder_get_url (mu_folder_t folder, mu_url_t *purl) +{ + if (folder == NULL) + return EINVAL; + if (purl == NULL) + return MU_ERR_OUT_PTR_NULL; + *purl = folder->url; + return 0; +} + +static int +is_known_folder (mu_url_t url, mu_folder_t *pfolder) +{ + int ret = 0; + mu_folder_t folder = NULL; + mu_iterator_t iterator; + + if (url == NULL || pfolder == NULL) + return ret; + + if (mu_list_get_iterator (known_folder_list, &iterator) != 0) + return ret; + + for (mu_iterator_first (iterator); !mu_iterator_is_done (iterator); + mu_iterator_next (iterator)) + { + mu_iterator_current (iterator, (void **)&folder); + /* Check if the same URL type. */ + if (folder && folder->url + && mu_url_is_same_scheme (url, folder->url) + && mu_url_is_same_user (url, folder->url) + && mu_url_is_same_host (url, folder->url) + && mu_url_is_same_path (url, folder->url) + && mu_url_is_same_port (url, folder->url)) + { + ret = 1; + break; + } + } + if (ret) + *pfolder = folder; + mu_iterator_destroy (&iterator); + return ret; +} + |