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 @@ -92,6 +92,8 @@ extern int mu_folder_get_property (mu_folder_t, mu_property_t *); 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 } 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 @@ -43,6 +43,8 @@ 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 *); 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 @@ -25,6 +25,7 @@ libmailbox_la_SOURCES = \ body.c\ envelope.c\ folder.c\ + fsfolder.c\ hdrfirst.c\ hdritr.c\ header.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 @@ -36,6 +36,7 @@ #include <mailutils/url.h> #include <mailutils/errno.h> #include <mailutils/property.h> +#include <mailutils/mailbox.h> #include <mailutils/sys/folder.h> @@ -49,7 +50,7 @@ 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 @@ -249,7 +250,9 @@ mu_folder_get_property (mu_folder_t folder, mu_property_t *prop) 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); } @@ -257,7 +260,9 @@ mu_folder_open (mu_folder_t folder, int 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); } @@ -381,8 +386,10 @@ mu_folder_enumerate (mu_folder_t folder, const char *name, 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; @@ -412,7 +419,9 @@ mu_folder_lsub (mu_folder_t folder, const char *dirname, const char *basename, { int status; - if (folder == NULL || folder->_lsub == NULL) + if (folder == NULL) + return EINVAL; + else if (folder->_lsub == NULL) return ENOSYS; else { @@ -428,31 +437,64 @@ mu_folder_lsub (mu_folder_t folder, const char *dirname, const char *basename, 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); } 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 @@ -41,6 +41,7 @@ #include <mailutils/util.h> #include <mailutils/sys/mailbox.h> +#include <mailutils/sys/folder.h> #include <mailutils/sys/url.h> /* Mailbox-specific flags */ @@ -76,7 +77,8 @@ mailbox_folder_create (mu_mailbox_t mbox, const char *name, 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; @@ -127,10 +129,16 @@ _mailbox_create_from_record (mu_mailbox_t *pmbox, } 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. */ @@ -152,11 +160,12 @@ 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 @@ -203,7 +212,71 @@ mu_mailbox_create_from_record (mu_mailbox_t *pmbox, mu_record_t record, 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; @@ -314,7 +387,24 @@ mu_mailbox_remove (mu_mailbox_t mbox) 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); } @@ -689,7 +779,7 @@ 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; } diff --git a/libmailutils/tests/.gitignore b/libmailutils/tests/.gitignore index d67519f63..6a1188db8 100644 --- a/libmailutils/tests/.gitignore +++ b/libmailutils/tests/.gitignore @@ -12,6 +12,7 @@ decode2047 encode2047 fltst fsaf +fsfolder imapio listop mailcap diff --git a/libmailutils/tests/Makefile.am b/libmailutils/tests/Makefile.am index 1cb296868..aca151a75 100644 --- a/libmailutils/tests/Makefile.am +++ b/libmailutils/tests/Makefile.am @@ -47,6 +47,7 @@ noinst_PROGRAMS = \ encode2047\ fltst\ fsaf\ + fsfolder\ imapio\ listop\ mailcap\ @@ -78,6 +79,9 @@ TESTSUITE_AT = \ encode2047.at\ fromflt.at\ fsaf.at\ + fsfolder00.at\ + fsfolder01.at\ + fsfolder02.at\ hdrflt.at\ imapio.at\ inline-comment.at\ diff --git a/libmailutils/tests/fsfolder.c b/libmailutils/tests/fsfolder.c new file mode 100644 index 000000000..367106c90 --- /dev/null +++ b/libmailutils/tests/fsfolder.c @@ -0,0 +1,293 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2011 Free Software Foundation, Inc. + + GNU Mailutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Mailutils 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <stdlib.h> +#include <string.h> +#include <mailutils/error.h> +#include <mailutils/errno.h> +#include <mailutils/folder.h> +#include <mailutils/stream.h> +#include <mailutils/stdstream.h> +#include <mailutils/list.h> +#include <mailutils/url.h> +#include <mailutils/util.h> +#include <mailutils/registrar.h> +#include <mailutils/sys/folder.h> +#include <mailutils/sys/registrar.h> + +int sort_option; +int prefix_len; + +struct command +{ + char *verb; + int nargs; + char *args; + void (*handler) (mu_folder_t folder, char **argv); +}; + +static int +compare_response (void const *a, void const *b) +{ + struct mu_list_response const *ra = a; + struct mu_list_response const *rb = b; + + if (ra->level < rb->level) + return -1; + if (ra->level > rb->level) + return 1; + return strcmp (ra->name, rb->name); +} + +static int +_print_list_entry (void *item, void *data) +{ + struct mu_list_response *resp = item; + int len = data ? *(int*) data : 0; |