diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-12-16 16:36:29 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-12-16 20:58:59 +0200 |
commit | 33a5d85239028bc3e34eec59909253d12d5434f4 (patch) | |
tree | 5bd640ccf3988c2a616d7c44575c9be35e02f16f | |
parent | a837453677a2888155aa565666acc956cdd3f896 (diff) | |
download | mailutils-33a5d85239028bc3e34eec59909253d12d5434f4.tar.gz mailutils-33a5d85239028bc3e34eec59909253d12d5434f4.tar.bz2 |
Share folder implementation between mbox, mh and maildir. Fix mu_folder_delete.
* include/mailutils/folder.h (_mu_fsfolder_init): New proto.
* include/mailutils/mailbox.h (mu_mailbox_create_at): New proto.
* libmailutils/mailbox/mailbox.c (mu_mailbox_create_at): New function.
* libmailutils/mailbox/Makefile.am (libmailbox_la_SOURCES): Add fsfolder.c
* libmailutils/mailbox/folder.c (mu_folder_delete): If folder does
not provide its own method for deletion, use mu_mailbox_remove.
(mu_folder_open, mu_folder_close, mu_folder_enumerate)
(mu_folder_lsub, mu_folder_subscribe, mu_folder_unsubscribe)
(mu_folder_rename): Return EINVAL if folder is NULL.
(mu_folder_match): Bugfix: don't pass folder flags to fnmatch.
* libmailutils/mailbox/fsfolder.c: New file. Implementation of
file-system based folders.
* libmailutils/mailbox/mailbox (_create_mailbox0): Propagate error
return from mu_registrar_lookup_url.
* libmailutils/tests/fsfolder00.at: New test case.
* libmailutils/tests/fsfolder01.at: New test case.
* libmailutils/tests/fsfolder02.at: New test case.
* libmailutils/tests/Makefile.am (noinst_PROGRAMS): Add fsfolder.
(TESTSUITE_AT): Add fsfolder tests.
* libmailutils/tests/testsuite.at: Include fsfolder tests.
* libproto/mbox/folder.c: Use fsfolder.
(_mh_is_scheme): Check file even if scheme matches.
* libproto/maildir/folder.c: Likewise.
(_maildir_is_scheme): Check file even if scheme matches.
* libproto/mh/folder.c: Likewise.
* testsuite/fldel.c: New file.
* testsuite/Makefile.am (noinst_PROGRAMS): Buld fldel.
-rw-r--r-- | include/mailutils/folder.h | 2 | ||||
-rw-r--r-- | include/mailutils/mailbox.h | 2 | ||||
-rw-r--r-- | libmailutils/mailbox/Makefile.am | 1 | ||||
-rw-r--r-- | libmailutils/mailbox/folder.c | 64 | ||||
-rw-r--r-- | libmailutils/mailbox/fsfolder.c | 528 | ||||
-rw-r--r-- | libmailutils/mailbox/mailbox.c | 116 | ||||
-rw-r--r-- | libmailutils/tests/.gitignore | 1 | ||||
-rw-r--r-- | libmailutils/tests/Makefile.am | 4 | ||||
-rw-r--r-- | libmailutils/tests/fsfolder.c | 293 | ||||
-rw-r--r-- | libmailutils/tests/fsfolder00.at | 53 | ||||
-rw-r--r-- | libmailutils/tests/fsfolder01.at | 74 | ||||
-rw-r--r-- | libmailutils/tests/fsfolder02.at | 39 | ||||
-rw-r--r-- | libmailutils/tests/testsuite.at | 5 | ||||
-rw-r--r-- | libproto/imap/tests/imapfolder.c | 1 | ||||
-rw-r--r-- | libproto/maildir/folder.c | 45 | ||||
-rw-r--r-- | libproto/mbox/folder.c | 517 | ||||
-rw-r--r-- | libproto/mh/folder.c | 37 | ||||
-rw-r--r-- | testsuite/.gitignore | 1 | ||||
-rw-r--r-- | testsuite/Makefile.am | 1 | ||||
-rw-r--r-- | testsuite/fldel.c | 54 |
20 files changed, 1271 insertions, 567 deletions
diff --git a/include/mailutils/folder.h b/include/mailutils/folder.h index e8f6cb914..90e21b93d 100644 --- a/include/mailutils/folder.h +++ b/include/mailutils/folder.h @@ -89,12 +89,14 @@ extern int mu_folder_set_property (mu_folder_t, mu_property_t); extern int mu_folder_get_property (mu_folder_t, mu_property_t *); /* FIXME: not implemented */ extern int mu_folder_decrement (mu_folder_t); extern void mu_list_response_free (void *data); + +int _mu_fsfolder_init (mu_folder_t folder); #ifdef __cplusplus } #endif #endif /* _MAILUTILS_FOLDER_H */ diff --git a/include/mailutils/mailbox.h b/include/mailutils/mailbox.h index 89698592a..f21dc89a6 100644 --- a/include/mailutils/mailbox.h +++ b/include/mailutils/mailbox.h @@ -40,12 +40,14 @@ int mu_construct_user_mailbox_url (char **pout, const char *name); extern int mu_mailbox_create (mu_mailbox_t *, const char *); extern int mu_mailbox_create_from_url (mu_mailbox_t *, mu_url_t); extern int mu_mailbox_create_from_record (mu_mailbox_t *pmbox, mu_record_t record, const char *name); extern int mu_mailbox_create_default (mu_mailbox_t *, const char *); +extern int mu_mailbox_create_at (mu_mailbox_t *pmbox, mu_folder_t folder, + const char *name); extern void mu_mailbox_destroy (mu_mailbox_t *); extern int mu_mailbox_open (mu_mailbox_t, int flag); extern int mu_mailbox_close (mu_mailbox_t); extern int mu_mailbox_remove (mu_mailbox_t mbox); diff --git a/libmailutils/mailbox/Makefile.am b/libmailutils/mailbox/Makefile.am index 27e6c044a..cb9e775fc 100644 --- a/libmailutils/mailbox/Makefile.am +++ b/libmailutils/mailbox/Makefile.am @@ -22,12 +22,13 @@ libmailbox_la_SOURCES = \ mbx_default.c\ mbxitr.c\ attribute.c\ body.c\ envelope.c\ folder.c\ + fsfolder.c\ hdrfirst.c\ hdritr.c\ header.c\ msgcpy.c\ msgattr.c\ msgbody.c\ diff --git a/libmailutils/mailbox/folder.c b/libmailutils/mailbox/folder.c index 3314fcd38..9d1fc1a1c 100644 --- a/libmailutils/mailbox/folder.c +++ b/libmailutils/mailbox/folder.c @@ -33,12 +33,13 @@ #include <mailutils/observer.h> #include <mailutils/registrar.h> #include <mailutils/stream.h> #include <mailutils/url.h> #include <mailutils/errno.h> #include <mailutils/property.h> +#include <mailutils/mailbox.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 *); @@ -46,13 +47,13 @@ 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); + return fnmatch (pattern, name[0] == '/' ? name + 1 : name, 0); } /* 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 @@ -246,21 +247,25 @@ mu_folder_get_property (mu_folder_t folder, mu_property_t *prop) /* Cover functions. */ int mu_folder_open (mu_folder_t folder, int flags) { - if (folder == NULL || folder->_open == NULL) + if (folder == NULL) + return EINVAL; + if (folder->_open == NULL) return ENOSYS; return folder->_open (folder, flags); } int mu_folder_close (mu_folder_t folder) { - if (folder == NULL || folder->_close == NULL) + if (folder == NULL) + return EINVAL; + if (folder->_close == NULL) return ENOSYS; return folder->_close (folder); } int mu_folder_set_stream (mu_folder_t folder, mu_stream_t stream) @@ -378,14 +383,16 @@ 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) + if (folder == NULL) return EINVAL; + else if (folder->_list == NULL) + return ENOSYS; else { mu_list_t list = NULL; if (pflist) { @@ -409,13 +416,15 @@ mu_folder_enumerate (mu_folder_t folder, const char *name, 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) + if (folder == NULL) + return EINVAL; + else if (folder->_lsub == NULL) return ENOSYS; else { status = mu_list_create (pflist); if (status) return status; @@ -425,37 +434,70 @@ mu_folder_lsub (mu_folder_t folder, const char *dirname, const char *basename, return status; } int mu_folder_subscribe (mu_folder_t folder, const char *name) { - if (folder == NULL || folder->_subscribe == NULL) + if (folder == NULL) return EINVAL; + if (folder->_subscribe == NULL) + return ENOSYS; return folder->_subscribe (folder, name); } int mu_folder_unsubscribe (mu_folder_t folder, const char *name) { - if (folder == NULL || folder->_unsubscribe == NULL) + if (folder == NULL) return EINVAL; + if (folder->_unsubscribe == NULL) + return ENOSYS; 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 rc; + + if (folder == NULL) + return EINVAL; + if (folder->_delete) + rc = folder->_delete (folder, name); + else + { + /* If there is no folder-specific _delete method, then try to create the + mailbox and call mailbox delete (remove) method. This is necessary + because certain types of mailboxes share a common folder (e.g. mbox, + maildir and mh all use filesystem folder), but have a different + internal structure. Supplying mu_folder_t with a knowledge of mailbox + internals will harm separation of concerns. On the other hand, + removing something without looking into it may well yield undesired + results. For example, a MH mailbox can hold another mailboxes, i.e. + be a folder itself. Removing it blindly would result in removing + these mailboxes as well, which is clearly not indended. + + To solve this folder and mailbox delete methods are tightly paired, + but without looking into each-others internal mechanisms. */ + mu_mailbox_t mbox; + rc = mu_mailbox_create_at (&mbox, folder, name); + if (rc == 0) + { + rc = mu_mailbox_remove (mbox); + mu_mailbox_destroy (&mbox); + } + } + return rc; } int mu_folder_rename (mu_folder_t folder, const char *oldname, const char *newname) { - if (folder == NULL || folder->_rename == NULL) + if (folder == NULL) + return EINVAL; + if (folder->_rename == NULL) return ENOSYS; return folder->_rename (folder, oldname, newname); } int mu_folder_get_url (mu_folder_t folder, mu_url_t *purl) diff --git a/libmailutils/mailbox/fsfolder.c b/libmailutils/mailbox/fsfolder.c new file mode 100644 index 000000000..fb43e3357 --- /dev/null +++ b/libmailutils/mailbox/fsfolder.c @@ -0,0 +1,528 @@ +/* Implementation of file-system folder for GNU Mailutils + Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2008, + 2010, 2011 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, see + <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <errno.h> +#include <sys/types.h> +#include <dirent.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <glob.h> +#include <fnmatch.h> +#include <stdio.h> +#include <stdlib.h> + +#include <mailutils/sys/folder.h> +#include <mailutils/sys/registrar.h> + +#include <mailutils/auth.h> +#include <mailutils/url.h> +#include <mailutils/stream.h> +#include <mailutils/util.h> +#include <mailutils/errno.h> +#include <mailutils/debug.h> +#include <mailutils/property.h> +#include <mailutils/iterator.h> + +/* File-system folder is shared between UNIX mbox, maildir and MH + mailboxes. It implements all usual folder methods, excepting + for _delete, which is implemented on the mailbox level. See + comment to mu_folder_delete in folder.c */ + +struct _mu_fsfolder +{ + char *dirname; + mu_property_t subscription; +}; + +static int +open_subscription (struct _mu_fsfolder *folder) +{ + int rc; + mu_property_t prop; + mu_stream_t str; + char *filename = mu_make_file_name (folder->dirname, ".mu-subscr"); + + rc = mu_file_stream_create (&str, filename, MU_STREAM_RDWR|MU_STREAM_CREAT); + if (rc) + return rc; + rc = mu_property_create_init (&prop, mu_assoc_property_init, str); + free (filename); + if (rc == 0) + folder->subscription = prop; + return rc; +} + + +static char * +get_pathname (const char *dirname, const char *basename) +{ + char *pathname = NULL, *p; + + /* Skip eventual protocol designator. */ + p = strchr (dirname, ':'); + if (p && p[1] == '/' && p[2] == '/') + dirname = p + 3; + + /* null basename gives dirname. */ + if (basename == NULL) + pathname = (dirname) ? strdup (dirname) : strdup ("."); + /* Absolute. */ + else if (basename[0] == '/') + pathname = strdup (basename); + /* Relative. */ + else + { + size_t baselen = strlen (basename); + size_t dirlen = strlen (dirname); + while (dirlen > 0 && dirname[dirlen-1] == '/') + dirlen--; + pathname = calloc (dirlen + baselen + 2, sizeof (char)); + if (pathname) + { + memcpy (pathname, dirname, dirlen); + pathname[dirlen] = '/'; + strcpy (pathname + dirlen + 1, basename); + } + } + return pathname; +} + +static void +_fsfolder_destroy (mu_folder_t folder) +{ + if (folder->data) + { + struct _mu_fsfolder *fsfolder = folder->data; + free (fsfolder->dirname); + mu_property_destroy (&fsfolder->subscription); + free (folder->data); + folder->data = NULL; + } +} + +/* Noop. */ +static int +_fsfolder_open (mu_folder_t folder, int flags MU_ARG_UNUSED) +{ + struct _mu_fsfolder *fsfolder = folder->data; + if (flags & MU_STREAM_CREAT) + { + return (mkdir (fsfolder->dirname, S_IRWXU) == 0) ? 0 : errno; + } + return 0; +} + +/* Noop. */ +static int +_fsfolder_close (mu_folder_t folder MU_ARG_UNUSED) +{ + int rc = 0; + struct _mu_fsfolder *fsfolder = folder->data; + + if (fsfolder->subscription) + rc = mu_property_save (fsfolder->subscription); + return rc; +} + +static int +_fsfolder_rename (mu_folder_t folder, const char *oldpath, + const char *newpath) +{ + struct _mu_fsfolder *fsfolder = folder->data; + if (oldpath && newpath) + { + int status = 0; + char *pathold = get_pathname (fsfolder->dirname, oldpath); + if (pathold) + { + char *pathnew = get_pathname (fsfolder->dirname, newpath); + if (pathnew) + { + if (access (pathnew, F_OK) == 0) + status = EEXIST; + else if (rename (pathold, pathnew) != 0) + status = errno; + free (pathnew); + } + else + status = ENOMEM; + free (pathold); + } + else + status = ENOMEM; + return status; + } + return EINVAL; +} + +struct inode_list /* Inode/dev number list used to cut off + recursion */ +{ + struct inode_list *next; + ino_t inode; + dev_t dev; +}; + +struct search_data +{ + mu_list_t result; + mu_folder_enumerate_fp enumfun; + void *enumdata; + char *dirname; + size_t dirlen; + void *pattern; + int flags; + size_t max_level; + size_t errcnt; + mu_folder_t folder; +}; + +static int +inode_list_lookup (struct inode_list *list, struct stat *st) +{ + for (; list; list = list->next) + if (list->inode == st->st_ino && list->dev == st->st_dev) + return 1; + return 0; +} + +static int +list_helper (struct search_data *data, mu_record_t record, + const char *dirname, size_t level, + struct inode_list *ilist) +{ + DIR *dirp; + struct dirent *dp; + int stop = 0; + + if (data->max_level && level > data->max_level) + return 0; + + dirp = opendir (dirname); + if (dirp == NULL) + { + mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR, + ("list_helper cannot open directory %s: %s", + dirname, mu_strerror (errno))); + data->errcnt++; + return 1; + } + + if (!record) + { + int type; + mu_registrar_lookup (dirname, MU_FOLDER_ATTRIBUTE_ALL, + &record, &type); + } + + while ((dp = readdir (dirp))) + { + char const *ename = dp->d_name; + char *fname; + struct stat st; + + if (ename[ename[0] != '.' ? 0 : ename[1] != '.' ? 1 : 2] == 0) + continue; + if (strncmp (ename, ".mu-", 4) == 0) + continue; + fname = get_pathname (dirname, ename); + if (lstat (fname, &st) == 0) + { + int f; + if (S_ISDIR (st.st_mode)) + f = MU_FOLDER_ATTRIBUTE_DIRECTORY; + else if (S_ISREG (st.st_mode)) + f = MU_FOLDER_ATTRIBUTE_FILE; + else if (S_ISLNK (st.st_mode)) + f = MU_FOLDER_ATTRIBUTE_LINK; + else + f = 0; + if (mu_record_list_p (record, ename, f)) + { + if (data->folder->_match == NULL + || data->folder->_match (fname + data->dirlen + + ((data->dirlen > 1 + && data->dirname[data->dirlen-1] != '/') ? + 1 : 0), + data->pattern, + data->flags) == 0) + { + char *refname = fname; + int type = 0; + struct mu_list_response *resp; + mu_record_t rec = NULL; + + resp = malloc (sizeof (*resp)); + if (resp == NULL) + { + mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR, + ("list_helper: %s", mu_strerror (ENOMEM))); + data->errcnt++; + free (fname); + continue; + } + + mu_registrar_lookup (refname, MU_FOLDER_ATTRIBUTE_ALL, + &rec, &type); + + resp->name = fname; + resp->level = level; + resp->separator = '/'; + resp->type = type; + resp->format = rec; + + if (resp->type == 0) + { + free (resp->name); + free (resp); + continue; + } + + if (data->enumfun) + { + if (data->enumfun (data->folder, resp, data->enumdata)) + { + free (resp->name); + free (resp); + stop = 1; + break; + } + } + + if (data->result) + { + fname = NULL; + mu_list_append (data->result, resp); + } + else + free (resp); + + if ((type & MU_FOLDER_ATTRIBUTE_DIRECTORY) + && !inode_list_lookup (ilist, &st)) + { + struct inode_list idata; + + idata.inode = st.st_ino; + idata.dev = st.st_dev; + idata.next = ilist; + stop = list_helper (data, rec, refname, level + 1, + &idata); + } + } + else if (S_ISDIR (st.st_mode)) + { + struct inode_list idata; + + idata.inode = st.st_ino; + idata.dev = st.st_dev; + idata.next = ilist; + stop = list_helper (data, NULL, fname, level + 1, &idata); + } + } + } + else + { + mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR, + ("list_helper cannot stat %s: %s", + fname, mu_strerror (errno))); + } + free (fname); + } + closedir (dirp); + return stop; +} + +static int +_fsfolder_list (mu_folder_t folder, const char *ref, + void *pattern, + int flags, + size_t max_level, + mu_list_t flist, + mu_folder_enumerate_fp enumfun, void *enumdata) +{ + struct _mu_fsfolder *fsfolder = folder->data; + struct inode_list iroot; + struct search_data sdata; + + memset (&iroot, 0, sizeof iroot); + sdata.dirname = get_pathname (fsfolder->dirname, ref); + sdata.dirlen = strlen (sdata.dirname); + sdata.result = flist; + sdata.enumfun = enumfun; + sdata.enumdata = enumdata; + sdata.pattern = pattern; + sdata.flags = flags; + sdata.max_level = max_level; + sdata.folder = folder; + sdata.errcnt = 0; + list_helper (&sdata, NULL, sdata.dirname, 0, &iroot); + free (sdata.dirname); + /* FIXME: error code */ + return 0; +} + +static int +_fsfolder_lsub (mu_folder_t folder, const char *ref, const char *name, + mu_list_t flist) +{ + struct _mu_fsfolder *fsfolder = folder->data; + int rc; + char *pattern; + mu_iterator_t itr; + + if (name == NULL || *name == '\0') + name = "*"; + + if (!fsfolder->subscription && (rc = open_subscription (fsfolder))) + return rc; + + pattern = mu_make_file_name (ref, name); + + rc = mu_property_get_iterator (fsfolder->subscription, &itr); + if (rc == 0) + { + for (mu_iterator_first (itr); !mu_iterator_is_done (itr); + mu_iterator_next (itr)) + { + const char *key, *val; + + mu_iterator_current_kv (itr, (const void **)&key, (void**)&val); + + if (fnmatch (pattern, key, 0) == 0) + { + struct mu_list_response *resp; + resp = malloc (sizeof (*resp)); + if (resp == NULL) + { + rc = ENOMEM; + break; + } + else if ((resp->name = strdup (key)) == NULL) + { + free (resp); + rc = ENOMEM; + break; + } + resp->type = MU_FOLDER_ATTRIBUTE_FILE; + resp->level = 0; + resp->separator = '/'; + rc = mu_list_append (flist, resp); + if (rc) + { + free (resp); + break; + } + } + } + mu_iterator_destroy (&itr); + } + free (pattern); + return rc; +} + +static int +_fsfolder_subscribe (mu_folder_t folder, const char *name) +{ + struct _mu_fsfolder *fsfolder = folder->data; + int rc; + + if (!fsfolder->subscription && (rc = open_subscription (fsfolder))) + return rc; + + return mu_property_set_value (fsfolder->subscription, name, "", 1); +} + +static int +_fsfolder_unsubscribe (mu_folder_t folder, const char *name) +{ + struct _mu_fsfolder *fsfolder = folder->data; + int rc; + + if (!fsfolder->subscription && (rc = open_subscription (fsfolder))) + return rc; + + return mu_property_unset (fsfolder->subscription, name); +} + +static int +_fsfolder_get_authority (mu_folder_t folder, mu_authority_t *pauth) +{ + int status = 0; + if (folder->authority == NULL) + status = mu_authority_create_null (&folder->authority, folder); + if (!status && pauth) + *pauth = folder->authority; + return status; +} + +int +_mu_fsfolder_init (mu_folder_t folder) +{ + struct _mu_fsfolder *dfolder; + int status = 0; + + /* We create an authority so the API is uniform across the mailbox + types. */ + status = _fsfolder_get_authority (folder, NULL); + if (status != 0) + return status; + + dfolder = folder->data = calloc (1, sizeof (*dfolder)); + if (dfolder == NULL) + return ENOMEM; + + status = mu_url_aget_path (folder->url, &dfolder->dirname); + if (status == MU_ERR_NOENT) + { + dfolder->dirname = malloc (2); + if (dfolder->dirname == NULL) + status = ENOMEM; + else + { + strcpy (dfolder->dirname, "."); + status = 0; + } + } + + if (status) + { + free (dfolder); + folder->data = NULL; + return status; + } + + folder->_destroy = _fsfolder_destroy; + + folder->_open = _fsfolder_open; + folder->_close = _fsfolder_close; + + folder->_list = _fsfolder_list; + folder->_lsub = _fsfolder_lsub; + folder->_subscribe = _fsfolder_subscribe; + folder->_unsubscribe = _fsfolder_unsubscribe; + folder->_delete = NULL; + folder->_rename = _fsfolder_rename; + return 0; +} + diff --git a/libmailutils/mailbox/mailbox.c b/libmailutils/mailbox/mailbox.c index a31682a12..990ae6a2e 100644 --- a/libmailutils/mailbox/mailbox.c +++ b/libmailutils/mailbox/mailbox.c @@ -38,12 +38,13 @@ #include <mailutils/url.h> #include <mailutils/attribute.h> #include <mailutils/message.h> #include <mailutils/util.h> #include <mailutils/sys/mailbox.h> +#include <mailutils/sys/folder.h> #include <mailutils/sys/url.h> /* Mailbox-specific flags */ #define _MU_MAILBOX_OPEN 0x10000000 #define _MU_MAILBOX_REMOVED 0x20000000 #define _MU_MAILBOX_MASK 0xF0000000 @@ -73,13 +74,14 @@ mailbox_folder_create (mu_mailbox_t mbox, const char *name, return rc; } int _mailbox_create_from_record (mu_mailbox_t *pmbox, mu_record_t record, - mu_url_t url, + mu_url_t url, + mu_folder_t folder, const char *name) { int (*m_init) (mu_mailbox_t) = NULL; mu_record_get_mailbox (record, &m_init); if (m_init) @@ -124,16 +126,22 @@ _mailbox_create_from_record (mu_mailbox_t *pmbox, { mu_mailbox_destroy (&mbox); return status; } mbox->url = url; - - /* Create the folder before initializing the concrete mailbox. - The mailbox needs it's back pointer. */ - status = mailbox_folder_create (mbox, name, record); + + if (folder) + { + folder->ref++; /* FIXME: No ref/unref function for folders */ + mbox->folder = folder; + } + else + /* Create the folder before initializing the concrete mailbox. + The mailbox needs it's back pointer. */ + status = mailbox_folder_create (mbox, name, record); if (status == 0) status = m_init (mbox); /* Create the concrete mailbox type. */ if (status != 0) { @@ -149,17 +157,18 @@ _mailbox_create_from_record (mu_mailbox_t *pmbox, } static int _create_mailbox0 (mu_mailbox_t *pmbox, mu_url_t url, const char *name) { mu_record_t record = NULL; - - if (mu_registrar_lookup_url (url, MU_FOLDER_ATTRIBUTE_FILE, &record, NULL) - == 0) - return _mailbox_create_from_record (pmbox, record, url, name); - return ENOSYS; + int rc; + + rc = mu_registrar_lookup_url (url, MU_FOLDER_ATTRIBUTE_FILE, &record, NULL); + if (rc == 0) + rc = _mailbox_create_from_record (pmbox, record, url, NULL, name); + return rc; } static int _create_mailbox (mu_mailbox_t *pmbox, const char *name) { int status; @@ -200,13 +209,77 @@ mu_mailbox_create_from_record (mu_mailbox_t *pmbox, mu_record_t record, mu_url_t url; int rc; rc = mu_url_create (&url, name); if (rc) return rc; - rc = _mailbox_create_from_record (pmbox, record, url, name); + rc = _mailbox_create_from_record (pmbox, record, url, NULL, name); + if (rc) + mu_url_destroy (&url); + return rc; +} + +int +mu_mailbox_create_at (mu_mailbox_t *pmbox, mu_folder_t folder, + const char *name) +{ + int rc; + mu_url_t url; + const char *oldpath; + + rc = mu_url_dup (folder->url, &url); + if (rc) + return rc; + do + { + char *path; + size_t oldlen, len; + mu_record_t record; + + rc = mu_url_sget_path (url, &oldpath); + if (rc) + break; + + oldlen = strlen (oldpath); + if (oldlen == 0) + { + path = strdup (name); + if (!path) + { + rc = ENOMEM; + break; + } + } + else + { + if (oldpath[oldlen-1] == '/') + oldlen--; + len = oldlen + 1 + strlen (name) + 1; + path = malloc (len); + if (!path) + { + rc = ENOMEM; + break; + } + memcpy (path, oldpath, oldlen); + path[oldlen++] = '/'; + strcpy (path + oldlen, name); + } + rc = mu_url_set_path (url, path); + free (path); + if (rc) + break; + + rc = mu_registrar_lookup_url (url, MU_FOLDER_ATTRIBUTE_FILE, + &record, NULL); + if (rc) + break; + rc = _mailbox_create_from_record (pmbox, record, url, folder, name); + } + while (0); + if (rc) mu_url_destroy (&url); return rc; } void @@ -311,13 +384,30 @@ mu_mailbox_remove (mu_mailbox_t mbox) return EINVAL; if (mbox->flags & _MU_MAILBOX_OPEN) return MU_ERR_OPEN; if (mbox->flags & _MU_MAILBOX_REMOVED) return MU_ERR_MBX_REMOVED; if (!mbox->_remove) - return MU_ERR_EMPTY_VFN; + { + /* Try the owning folder delete method. See comment to mu_folder_delete + in folder.c. This may result in a recursive call to mu_mailbox_remove + which is blocked by setting the _MU_MAILBOX_REMOVED flag. */ + + int rc; + const char *path; + + rc = mu_url_sget_path (mbox->url, &path); + if (rc == 0) + { + mbox->flags |= _MU_MAILBOX_REMOVED; + rc = mu_folder_delete (mbox->folder, path); + if (rc) + mbox->flags &= ~_MU_MAILBOX_REMOVED; + } + return rc; + } return mbox->_remove (mbox); } int mu_mailbox_flush (mu_mailbox_t mbox, int expunge) { @@ -686,13 +776,13 @@ mu_mailbox_get_folder (mu_mailbox_t mbox, mu_folder_t *pfolder) int mu_mailbox_set_folder (mu_mailbox_t mbox, mu_folder_t folder) { if (mbox == NULL) return EINVAL; - mbox->folder = folder; + mbox->folder = folder; return 0; } int mu_mailbox_lock (mu_mailbox_t mbox) { diff --git a/libmailutils/tests/.gitignore b/libmailutils/tests/.gitignore index d67519f63..6a1188db8 100644 --- a/libmailutils/tests/.gitignore +++ b/libmailutils/tests/.gitignore @@ -9,12 +9,13 @@ addr argcv de |