summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2017-01-04 10:54:18 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2017-01-04 13:56:32 +0200
commit9a94d41a0c8cd941db019478c7a75b1036ddf580 (patch)
tree0487aacf4e00e6dbe9118eb926125c5b370d356b
parent068c059972fbc53d734ab816d7583ec2fb918b12 (diff)
downloadmailutils-9a94d41a0c8cd941db019478c7a75b1036ddf580.tar.gz
mailutils-9a94d41a0c8cd941db019478c7a75b1036ddf580.tar.bz2
Rewrite namespace support
* imap4d/imap4d.c (imap4d_homedir) (modify_homedir, mailbox_mode): Remove. (imap4d_srv_param): New configuration statement "namespace" Remove statements: homedir, personal-namespace, shared-namespace, other-mailbox-mode, shared-mailbox-mode. * imap4d/imap4d.h (imap4d_homedir, modify_homedir) (mailbox_mode): Remove. (namespace, namespace_init_session, util_getfullpath) (namespace_getfullpath, namespace_checkfullpath): Remove. (namespace_prefix, namespace): New structs. (namespace_lookup, namespace_translate_name) (namespace_get_url, translate_delim): New protos. * imap4d/list.c (refinfo): Revamp. (list_fun): Rewrite. * imap4d/namespace.c: Rewrite from scratch. * imap4d/append.c: Use new namespace functions. * imap4d/util.c (util_getfullpath): Remove. * imap4d/copy.c: Likewise. * imap4d/create.c: Likewise. * imap4d/delete.c: Likewise. * imap4d/quota.c: Likewise. * imap4d/rename.c: Likewise. * imap4d/select.c: Likewise. * imap4d/status.c: Likewise. * imap4d/tests/atlocal.in (make_config): New function. * imap4d/tests/testsuite.at (IMAP4D_CONFIG) (IMAP4D_RUN): New functions. * imap4d/tests/list.at: Update tests 16, 17, 18, 19 * imap4d/tests/IDEF0955.at: Use IMAP4D_RUN. * imap4d/tests/IDEF0956.at: Likewise. * imap4d/tests/append00.at: Likewise. * imap4d/tests/append01.at: Likewise. * imap4d/tests/create01.at: Likewise. * imap4d/tests/create02.at: Likewise.
-rw-r--r--imap4d/append.c2
-rw-r--r--imap4d/copy.c6
-rw-r--r--imap4d/create.c6
-rw-r--r--imap4d/delete.c2
-rw-r--r--imap4d/imap4d.c239
-rw-r--r--imap4d/imap4d.h40
-rw-r--r--imap4d/list.c131
-rw-r--r--imap4d/namespace.c576
-rw-r--r--imap4d/quota.c6
-rw-r--r--imap4d/rename.c9
-rw-r--r--imap4d/select.c2
-rw-r--r--imap4d/status.c2
-rw-r--r--imap4d/tests/IDEF0955.at6
-rw-r--r--imap4d/tests/IDEF0956.at8
-rw-r--r--imap4d/tests/append00.at9
-rw-r--r--imap4d/tests/append01.at9
-rw-r--r--imap4d/tests/atlocal.in29
-rw-r--r--imap4d/tests/create01.at5
-rw-r--r--imap4d/tests/create02.at5
-rw-r--r--imap4d/tests/list.at54
-rw-r--r--imap4d/tests/testsuite.at30
-rw-r--r--imap4d/util.c20
22 files changed, 755 insertions, 441 deletions
diff --git a/imap4d/append.c b/imap4d/append.c
index 719a73eae..6ebd4b2fe 100644
--- a/imap4d/append.c
+++ b/imap4d/append.c
@@ -253,7 +253,7 @@ imap4d_append (struct imap4d_session *session,
msg_text = imap4d_tokbuf_getarg (tok, i);
- mboxname = namespace_getfullpath (mboxname, NULL);
+ mboxname = namespace_get_url (mboxname, NULL);
if (!mboxname)
return io_completion_response (command, RESP_NO, "Couldn't open mailbox");
diff --git a/imap4d/copy.c b/imap4d/copy.c
index 82e2511f2..12caabd79 100644
--- a/imap4d/copy.c
+++ b/imap4d/copy.c
@@ -211,7 +211,7 @@ imap4d_copy0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
char *end;
mu_mailbox_t cmbox = NULL;
int arg = IMAP4_ARG_1 + !!isuid;
- int ns;
+ int mode = 0;
*err_text = NULL;
if (imap4d_tokbuf_argc (tok) != arg + 2)
@@ -240,7 +240,7 @@ imap4d_copy0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
return RESP_BAD;
}
- mailbox_name = namespace_getfullpath (name, &ns);
+ mailbox_name = namespace_get_url (name, &mode);
if (!mailbox_name)
{
@@ -255,7 +255,7 @@ imap4d_copy0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
if (status == 0)
{
/* It SHOULD NOT automatifcllly create the mailbox. */
- status = mu_mailbox_open (cmbox, MU_STREAM_RDWR | mailbox_mode[ns]);
+ status = mu_mailbox_open (cmbox, MU_STREAM_RDWR | mode);
if (status == 0)
{
mu_list_t msglist;
diff --git a/imap4d/create.c b/imap4d/create.c
index b115e1147..6cacab7b6 100644
--- a/imap4d/create.c
+++ b/imap4d/create.c
@@ -39,7 +39,7 @@ imap4d_create (struct imap4d_session *session,
{
char *name;
int isdir = 0;
- int ns;
+ int mode = 0;
int rc = RESP_OK;
const char *msg = "Completed";
@@ -67,7 +67,7 @@ imap4d_create (struct imap4d_session *session,
isdir = 1;
/* Allocates memory. */
- name = namespace_getfullpath (name, &ns);
+ name = namespace_get_url (name, &mode);
if (!name)
return io_completion_response (command, RESP_NO, "Cannot create mailbox");
@@ -96,7 +96,7 @@ imap4d_create (struct imap4d_session *session,
}
else if ((rc = mu_mailbox_open (mbox,
MU_STREAM_RDWR | MU_STREAM_CREAT
- | mailbox_mode[ns])))
+ | mode)))
{
mu_diag_output (MU_DIAG_ERR,
_("Cannot open mailbox %s: %s"),
diff --git a/imap4d/delete.c b/imap4d/delete.c
index bbf5ae328..151b2fb73 100644
--- a/imap4d/delete.c
+++ b/imap4d/delete.c
@@ -49,7 +49,7 @@ imap4d_delete (struct imap4d_session *session,
return io_completion_response (command, RESP_NO, "Already exist");
/* Allocates memory. */
- name = namespace_getfullpath (name, NULL);
+ name = namespace_get_url (name, NULL);
if (!name)
return io_completion_response (command, RESP_NO, "Cannot remove");
diff --git a/imap4d/imap4d.c b/imap4d/imap4d.c
index 0dce6cabc..eca84c9d6 100644
--- a/imap4d/imap4d.c
+++ b/imap4d/imap4d.c
@@ -27,8 +27,6 @@ int imap4d_transcript;
mu_mailbox_t mbox; /* Current mailbox */
char *real_homedir; /* Homedir as returned by user database */
-char *imap4d_homedir; /* Homedir as visible for the remote party */
-char *modify_homedir; /* Expression to produce imap4d_homedir */
int state = STATE_NONAUTH; /* Current IMAP4 state */
struct mu_auth_data *auth_data;
@@ -40,8 +38,6 @@ mu_list_t user_retain_groups;
int home_dir_mode = S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
-int mailbox_mode[NS_MAX];
-
/* Saved command line. */
int imap4d_argc;
char **imap4d_argv;
@@ -359,7 +355,179 @@ imap_check_group_list (mu_list_t l)
return MU_ERR_NOENT;
return rc;
}
+
+static int
+cb_prefix_delim (void *data, mu_config_value_t *val)
+{
+ if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
+ return 1;
+ if (val->v.string[0] == 0)
+ mu_error (_("delimiter cannot be empty"));
+ else if (val->v.string[1] != 0)
+ mu_error (_("delimiter must be a single character"));
+ else
+ *(int*) data = val->v.string[0];
+ return 0;
+}
+
+static int
+cb_prefix_scheme (void *data, mu_config_value_t *val)
+{
+ struct namespace_prefix *pfx = data;
+ char *scheme;
+ mu_record_t rec;
+ int rc;
+ int (*mbx) (mu_mailbox_t);
+
+ if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
+ return 1;
+ scheme = mu_strdup (val->v.string);
+ rc = mu_registrar_lookup_scheme (scheme, &rec);
+ if (rc == MU_ERR_NOENT)
+ {
+ mu_error (_("unknown mailbox type"));
+ return 1;
+ }
+ else if (rc)
+ {
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_registrar_lookup_scheme", scheme, rc);
+ return 1;
+ }
+
+ rc = mu_record_get_mailbox (rec, &mbx);
+ if (rc)
+ {
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_record_get_mailbox", scheme, rc);
+ return 1;
+ }
+
+ if (!mbx || !mu_record_is_local (rec))
+ {
+ mu_error (_("not a local mailbox type"));
+ return 1;
+ }
+
+ pfx->scheme = scheme;
+ pfx->record = rec;
+
+ return 0;
+}
+
+static struct mu_cfg_param prefix_param[] = {
+ { "directory", mu_c_string,
+ NULL, mu_offsetof (struct namespace_prefix, dir), NULL,
+ N_("Directory in the file system") },
+ { "delimiter", mu_cfg_callback,
+ NULL, mu_offsetof (struct namespace_prefix, delim), cb_prefix_delim,
+ N_("Hierarchy delimiter character") },
+ { "mailbox-type", mu_cfg_callback,
+ NULL, 0, cb_prefix_scheme,
+ N_("Type of mailboxes residing under this prefix") },
+ { NULL }
+};
+
+static struct mu_cfg_param namespace_param[] = {
+ { "mailbox-mode", mu_cfg_callback,
+ NULL, mu_offsetof (struct namespace, mode), cb_mailbox_mode,
+ N_("File mode for newly created mailboxes in this namespace"),
+ N_("mode: g(+|=)[wr]+,o(+|=)[wr]+") },
+ { "prefix", mu_cfg_section },
+ { NULL }
+};
+static int
+prefix_section_parser (enum mu_cfg_section_stage stage,
+ const mu_cfg_node_t *node,
+ const char *section_label, void **section_data,
+ void *call_data,
+ mu_cfg_tree_t *tree)
+{
+ struct namespace_prefix *pfx;
+
+ switch (stage)
+ {
+ case mu_cfg_section_start:
+ {
+ struct namespace *nspace = *section_data;
+
+ if (node->label == NULL || node->label->type != MU_CFG_STRING)
+ return 1;
+
+ pfx = mu_zalloc (sizeof (*pfx));
+ pfx->prefix = mu_strdup (node->label->v.string);
+ mu_list_append (nspace->prefixes, pfx);
+ *section_data = pfx;
+ }
+ break;
+
+ case mu_cfg_section_end:
+ pfx = *section_data;
+ if (!pfx->delim)
+ pfx->delim = '/';
+ if (!pfx->dir)
+ {
+ if (pfx->prefix)
+ pfx->dir = mu_strdup (pfx->prefix);
+ else
+ {
+ mu_error (_("bad prefix definition"));
+ return 1;
+ }
+ }
+ else if (!pfx->prefix)
+ {
+ pfx->prefix = mu_alloc (strlen (pfx->dir) + 1);
+ translate_delim (pfx->prefix, pfx->dir, pfx->delim, '/');
+ }
+ }
+ return 0;
+}
+
+static int
+namespace_section_parser (enum mu_cfg_section_stage stage,
+ const mu_cfg_node_t *node,
+ const char *section_label, void **section_data,
+ void *call_data,
+ mu_cfg_tree_t *tree)
+{
+ if (stage == mu_cfg_section_start)
+ {
+ struct namespace *ns;
+
+ if (node->label == NULL || node->label->type != MU_CFG_STRING)
+ return 1;
+
+ ns = namespace_lookup (node->label->v.string);
+ if (!ns)
+ {
+ mu_error (_("unknown namespace"));
+ return 0;
+ }
+
+ *section_data = ns;
+ }
+ return 0;
+}
+
+static void
+namespace_cfg_init (void)
+{
+ struct mu_cfg_section *section;
+
+ if (mu_create_canned_section ("prefix", &section))
+ abort ();
+ section->docstring = N_("Define a single prefix");
+ mu_cfg_section_add_params (section, prefix_param);
+ section->parser = prefix_section_parser;
+
+ if (mu_create_canned_section ("namespace", &section))
+ abort ();
+ section->docstring = N_("Define a namespace");
+ section->label = "private | other | shared";
+ section->parser = namespace_section_parser;
+ mu_cfg_section_add_params (section, namespace_param);
+}
+
static struct mu_cfg_param imap4d_srv_param[] = {
#ifdef WITH_TLS
{ "tls", mu_cfg_callback,
@@ -385,8 +553,12 @@ static struct mu_cfg_param imap4d_cfg_param[] = {
0, NULL,
N_("Deny access if the user group is in this list.") },
+ { "namespace", mu_cfg_section },
+
+#if 0
{ "homedir", mu_c_string, &modify_homedir, 0, NULL,
N_("Modify home directory.") },
+
{ "personal-namespace", MU_CFG_LIST_OF(mu_c_string), &namespace[NS_PRIVATE],
0, NULL,
N_("Set personal namespace.") },
@@ -396,6 +568,7 @@ static struct mu_cfg_param imap4d_cfg_param[] = {
{ "shared-namespace", MU_CFG_LIST_OF(mu_c_string), &namespace[NS_SHARED],
0, NULL,
N_("Set shared namespace.") },
+ FIXME
{ "other-mailbox-mode", mu_cfg_callback, &mailbox_mode[NS_OTHER], 0,
cb_mailbox_mode,
N_("File mode for mailboxes in other namespace."),
@@ -404,6 +577,7 @@ static struct mu_cfg_param imap4d_cfg_param[] = {
cb_mailbox_mode,
N_("File mode for mailboxes in shared namespace."),
N_("mode: g(+|=)[wr]+,o(+|=)[wr]+") },
+#endif
{ "login-disabled", mu_c_bool, &login_disabled, 0, NULL,
N_("Disable LOGIN command.") },
{ "create-home-dir", mu_c_bool, &create_home_dir, 0, NULL,
@@ -530,52 +704,6 @@ imap4d_session_setup0 ()
real_homedir = mu_normalize_path (mu_strdup (auth_data->dir));
if (imap4d_check_home_dir (real_homedir, auth_data->uid, auth_data->gid))
return 1;
-
- if (modify_homedir)
- {
- char *expr = mu_tilde_expansion (modify_homedir, MU_HIERARCHY_DELIMITER,
- real_homedir);
- struct mu_wordsplit ws;
- const char *env[5];
-
- env[0] = "user";
- env[1] = auth_data->name;
- env[2] = "home";
- env[3] = real_homedir;
- env[4] = NULL;
-
- ws.ws_env = env;
- if (mu_wordsplit (expr, &ws,
- MU_WRDSF_NOSPLIT | MU_WRDSF_NOCMD |
- MU_WRDSF_ENV | MU_WRDSF_ENV_KV))
- {
- mu_error (_("cannot expand line `%s': %s"), expr,
- mu_wordsplit_strerror (&ws));
- return 1;
- }
- else if (ws.ws_wordc == 0)
- {
- mu_error (_("expanding %s yields empty string"), expr);
- return 1;
- }
- imap4d_homedir = mu_strdup (ws.ws_wordv[0]);
- if (!imap4d_homedir)
- {
- mu_error ("%s", mu_strerror (errno));
- return 1;
- }
- }
- else
- imap4d_homedir = mu_strdup (real_homedir);
-
- if (strcmp (imap4d_homedir, real_homedir)
- && imap4d_check_home_dir (imap4d_homedir,
- auth_data->uid, auth_data->gid))
- {
- free (imap4d_homedir);
- free (real_homedir);
- return 1;
- }
if (auth_data->change_uid)
{
@@ -591,7 +719,6 @@ imap4d_session_setup0 ()
if (rc)
{
mu_error(_("cannot create list: %s"), mu_strerror (rc));
- free (imap4d_homedir);
free (real_homedir);
return 1;
}
@@ -603,7 +730,6 @@ imap4d_session_setup0 ()
{
/* FIXME: When mu_get_user_groups goes to the library, add a
diag message here */
- free (imap4d_homedir);
free (real_homedir);
return 1;
}
@@ -614,15 +740,13 @@ imap4d_session_setup0 ()
{
mu_error (_("can't switch to user %s privileges: %s"),
auth_data->name, mu_strerror (rc));
- free (imap4d_homedir);
free (real_homedir);
return 1;
}
}
}
- util_chdir (imap4d_homedir);
- namespace_init_session (imap4d_homedir);
+ util_chdir (real_homedir);
mu_diag_output (MU_DIAG_INFO,
_("user `%s' logged in (source: %s)"), auth_data->name,
@@ -936,6 +1060,7 @@ main (int argc, char **argv)
mu_tcpwrapper_cfg_init ();
manlock_cfg_init ();
mu_acl_cfg_init ();
+ namespace_cfg_init ();
mu_m_server_create (&server, program_version);
mu_m_server_set_config_size (server, sizeof (struct imap4d_srv_config));
@@ -959,6 +1084,8 @@ main (int argc, char **argv)
mu_error (_("too many arguments"));
exit (EX_USAGE);
}
+
+ namespace_init ();
if (test_mode)
mu_m_server_set_mode (server, MODE_INTERACTIVE);
@@ -966,8 +1093,6 @@ main (int argc, char **argv)
if (login_disabled)
imap4d_capability_add (IMAP_CAPA_LOGINDISABLED);
- namespace_init ();
-
if (mu_gsasl_enabled ())
{
auth_gssapi_init ();
diff --git a/imap4d/imap4d.h b/imap4d/imap4d.h
index 8630375fa..788271d75 100644
--- a/imap4d/imap4d.h
+++ b/imap4d/imap4d.h
@@ -197,17 +197,13 @@ struct imap4d_session
extern struct imap4d_command imap4d_command_table[];
extern mu_mailbox_t mbox;
-extern char *imap4d_homedir;
extern char *real_homedir;
-extern char *modify_homedir;
extern int state;
extern size_t children;
extern int is_virtual;
extern struct mu_auth_data *auth_data;
extern const char *program_version;
-extern int mailbox_mode[NS_MAX];
-
extern int login_disabled;
extern enum imap4d_preauth preauth_mode;
extern char *preauth_program;
@@ -394,15 +390,34 @@ extern int imap4d_bye0 (int reason, struct imap4d_command *command);
void imap4d_enter_critical (void);
void imap4d_leave_critical (void);
-
/* Namespace functions */
-extern mu_list_t namespace[NS_MAX];
-
-extern int namespace_init_session (char *path);
-extern void namespace_init (void);
-extern char *namespace_getfullpath (const char *name, int *pns);
-extern char *namespace_checkfullpath (const char *name, const char *pattern,
- int *pns);
+struct namespace_prefix
+{
+ char *prefix; /* Prefix string */
+ int delim; /* Delimiter character */
+ char *dir; /* Directory in the file system */
+ char *scheme; /* Mailbox URL scheme (type) */
+ mu_record_t record; /* The corresponding record */
+ int ns; /* Namespace */
+};
+
+struct namespace
+{
+ char const *name;
+ int mode; /* File mode for creating new mailboxes */
+ mu_list_t prefixes; /* List of prefixes (struct namespace_prefix */
+};
+
+void namespace_init (void);
+struct namespace *namespace_lookup (char const *name);
+
+char *namespace_translate_name (char const *name, int url,
+ struct namespace_prefix const **pfx);
+char *namespace_get_url (char const *name, int *mode);
+
+void translate_delim (char *dst, char const *src, int dst_delim, int src_delim);
+
+
int imap4d_session_setup (char *username);
int imap4d_session_setup0 (void);
void imap4d_child_signal_setup (RETSIGTYPE (*handler) (int signo));
@@ -417,7 +432,6 @@ extern void imap4d_capability_init (void);
extern int util_start (char *);
extern int util_getstate (void);
extern int util_do_command (struct imap4d_session *, imap4d_tokbuf_t);
-extern char *util_getfullpath (const char *);
extern struct imap4d_command *util_getcommand (char *,
struct imap4d_command []);
diff --git a/imap4d/list.c b/imap4d/list.c
index 3b55a6cea..07cea67c2 100644
--- a/imap4d/list.c
+++ b/imap4d/list.c
@@ -23,8 +23,9 @@ struct refinfo
{
char *refptr; /* Original reference */
size_t reflen; /* Length of the original reference */
- size_t pfxlen; /* Length of the current prefix */
- size_t homelen; /* Length of homedir */
+ struct namespace_prefix const *pfx;
+ size_t pfxlen;
+ size_t dirlen; /* Length of the current directory prefix */
char *buf;
size_t bufsize;
};
@@ -35,12 +36,15 @@ list_fun (mu_folder_t folder, struct mu_list_response *resp, void *data)
char *name;
struct refinfo *refinfo = data;
size_t size;
+ char *p;
+
+ if (refinfo->pfx->record && refinfo->pfx->record != resp->format)
+ return 0;
name = resp->name;
size = strlen (name);
- if (size == refinfo->homelen + 6
- && memcmp (name, imap4d_homedir, refinfo->homelen) == 0
- && memcmp (name + refinfo->homelen + 1, "INBOX", 5) == 0)
+ if (size == refinfo->pfxlen + 6
+ && memcmp (name + refinfo->pfxlen + 1, "INBOX", 5) == 0)
return 0;
io_sendf ("* %s", "LIST (");
@@ -52,10 +56,10 @@ list_fun (mu_folder_t folder, struct mu_list_response *resp, void *data)
else if (resp->type & MU_FOLDER_ATTRIBUTE_DIRECTORY)
io_sendf ("\\NoSelect");
- io_sendf (") \"%c\" ", resp->separator);
+ io_sendf (") \"%c\" ", refinfo->pfx->delim);
- name = resp->name + refinfo->pfxlen;
- size = strlen (name) + refinfo->reflen + 1;
+ name = resp->name + refinfo->dirlen + 1;
+ size = strlen (name) + refinfo->pfxlen + 2;
if (size > refinfo->bufsize)
{
if (refinfo->buf == NULL)
@@ -82,10 +86,17 @@ list_fun (mu_folder_t folder, struct mu_list_response *resp, void *data)
}
}
- if ((refinfo->reflen == 0 || refinfo->refptr[refinfo->reflen - 1] == '/')
- && name[0] == '/')
- name++;
- strcpy (refinfo->buf + refinfo->reflen, name);
+ if (refinfo->pfxlen)
+ {
+ p = mu_stpcpy (refinfo->buf, refinfo->pfx->prefix);
+ if (*name)
+ *p++ = refinfo->pfx->delim;
+ }
+ else
+ p = refinfo->buf;
+ if (*name)
+ translate_delim (p, name, refinfo->pfx->delim, resp->separator);
+
name = refinfo->buf;
if (strpbrk (name, "\"{}"))
@@ -168,69 +179,84 @@ imap4d_list (struct imap4d_session *session,
{
int status;
mu_folder_t folder;
+ mu_url_t url;
char *cwd;
+ char const *dir;
struct refinfo refinfo;
size_t i;
+ struct namespace_prefix const *pfx;
+
+ if (*wcard == '~')
+ {
+ for (i = 1;
+ mu_c_is_class (wcard[i], MU_CTYPE_ALPHA|MU_CTYPE_DIGIT)
+ || wcard[i] == '_'; i++)
+ ;
+ ref = mu_alloc (i + 1);
+ memcpy (ref, wcard, i);
+ ref[i] = 0;
+ wcard += i;
+ }
+ else
+ ref = mu_strdup (ref);
+
+ cwd = namespace_translate_name (ref, 0, &pfx);
+ if (!cwd)
+ {
+ free (ref);
+ return io_completion_response (command, RESP_NO,
+ "The requested item could not be found.");
+ }
+ free (cwd);
- switch (*wcard)
+ if (*wcard == pfx->delim && ref[0] != '~')
{
/* Absolute Path in wcard, dump the old ref. */
- case '/':
- ref = mu_strdup ("/");
- wcard++;
- break;
-
- /* Absolute Path, but take care of things like ~guest/Mail,
- ref becomes ref = ~guest. */
- case '~':
- {
- char *s = strchr (wcard, '/');
- if (s)
- {
- ref = calloc (s - wcard + 1, 1);
- memcpy (ref, wcard, s - wcard);
- ref [s - wcard] = '\0';
- wcard = s + 1;
- }
- else
- {
- ref = mu_strdup (wcard);
- wcard += strlen (wcard);
- }
- }
- break;
-
- default:
- ref = mu_strdup (ref);
+ ref[0] = 0;
}
-
+
/* Find the longest directory prefix */
i = strcspn (wcard, "%*");
- while (i > 0 && wcard[i - 1] != '/')
+ while (i > 0 && wcard[i - 1] != pfx->delim)
i--;
/* Append it to the reference */
if (i)
{
size_t reflen = strlen (ref);
- int addslash = (reflen > 0 && ref[reflen-1] != '/');
+ int addslash = (reflen > 0
+ && ref[reflen-1] != pfx->delim
+ && *wcard != pfx->delim);
size_t len = i + reflen + addslash;
ref = mu_realloc (ref, len);
if (addslash)
- ref[reflen++] = '/';
+ ref[reflen++] = pfx->delim;
memcpy (ref + reflen, wcard, i - 1); /* omit the trailing / */
ref[len-1] = 0;
+
+ wcard += i;
}
- /* Allocates. */
- cwd = namespace_checkfullpath (ref, wcard, NULL);
+ cwd = namespace_translate_name (ref, 0, &pfx);
if (!cwd)
{
free (ref);
return io_completion_response (command, RESP_NO,
- "The requested item could not be found.");
+ "The requested item could not be found.");
}
-
+
+ if (pfx->ns == NS_OTHER
+ && strcmp (ref, pfx->prefix) == 0
+ && *mu_str_skip_cset_comp (wcard, "*%"))
+ {
+ /* [A] server MAY return NO to such a LIST command, requiring that a
+ user name be included with the Other Users' Namespace prefix
+ before listing any other user's mailboxes */
+ free (ref);
+ return io_completion_response (command, RESP_NO,
+ "The requested item could not be found.");
+ }
+
status = mu_folder_create (&folder, cwd);
if (status)
{
@@ -246,8 +272,13 @@ imap4d_list (struct imap4d_session *session,
refinfo.refptr = ref;
refinfo.reflen = strlen (ref);
- refinfo.pfxlen = strlen (cwd);
- refinfo.homelen = strlen (imap4d_homedir);
+ refinfo.pfx = pfx;
+
+ mu_folder_get_url (folder, &url);
+ mu_url_sget_path (url, &dir);
+ refinfo.dirlen = strlen (dir);
+
+ refinfo.pfxlen = strlen (pfx->prefix);
/* The special name INBOX is included in the output from LIST, if
INBOX is supported by this server for this user and if the
diff --git a/imap4d/namespace.c b/imap4d/namespace.c
index 383bccd63..cd3ae4373 100644
--- a/imap4d/namespace.c
+++ b/imap4d/namespace.c
@@ -16,93 +16,407 @@
along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
#include "imap4d.h"
+#include <mailutils/assoc.h>
-typedef int (*nsfp_t) (void *closure, int ns, char *path, int delim);
+struct namespace namespace[NS_MAX] = {
+ [NS_PRIVATE] = { "private" },
+ [NS_OTHER] = { "other" },
+ [NS_SHARED] = { "shared" }
+};
+
+static mu_assoc_t prefixes;
-mu_list_t namespace[NS_MAX];
+struct namespace *
+namespace_lookup (char const *name)
+{
+ size_t i;
+
+ for (i = 0; i < NS_MAX; i++)
+ if (strcmp (namespace[i].name, name) == 0)
+ {
+ if (!namespace[i].prefixes)
+ {
+ int rc = mu_list_create (&namespace[i].prefixes);
+ if (rc)
+ {
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_list_create", NULL, rc);
+ abort ();
+ }
+ }
+ return &namespace[i];
+ }
+ return NULL;
+}
+
+static int
+cmplen (const char *aname, const void *adata,
+ const char *bname, const void *bdata,
+ void *closure)
+{
+ return strlen (bname) - strlen (aname);
+}
-static const char *
-printable_pathname (const char *str)
+void
+translate_delim (char *dst, char const *src, int dst_delim, int src_delim)
{
- if (strncmp (str, imap4d_homedir, strlen (imap4d_homedir)) == 0)
+ do
+ *dst++ = *src == src_delim ? dst_delim : *src;
+ while (*src++);
+}
+
+static void
+trim_delim (char *str, int delim)
+{
+ size_t len = strlen (str);
+ while (len && str[len-1] == '/')
+ len--;
+ str[len] = 0;
+}
+
+void
+namespace_init (void)
+{
+ int i;
+ int rc;
+ struct namespace_prefix *pfx;
+
+ if (mu_assoc_create (&prefixes, 0))
+ imap4d_bye (ERR_NO_MEM);
+ for (i = 0; i < NS_MAX; i++)
+ {
+ mu_iterator_t itr;
+
+ if (mu_list_is_empty (namespace[i].prefixes))
+ continue;
+
+ if (mu_list_get_iterator (namespace[i].prefixes, &itr))
+ imap4d_bye (ERR_NO_MEM);
+
+
+ for (mu_iterator_first (itr);
+ !mu_iterator_is_done (itr); mu_iterator_next (itr))
+ {
+ rc = mu_iterator_current (itr, (void **)&pfx);
+ if (rc)
+ {
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_iterator_current", NULL, rc);
+ continue;
+ }
+ pfx->ns = i;
+
+ trim_delim (pfx->prefix, pfx->delim);
+ trim_delim (pfx->dir, '/');
+
+ rc = mu_assoc_install (prefixes, pfx->prefix, pfx);
+ if (rc == MU_ERR_EXISTS)
+ {
+ mu_error (_("%s: namespace prefix appears twice"), pfx->prefix);
+ exit (EX_CONFIG);
+ }
+ else if (rc)
+ {
+ mu_error (_("%s: can't install prefix: %s"),
+ pfx->prefix, mu_strerror (rc));
+ exit (EX_CONFIG);
+ }
+ }
+ }
+
+ pfx = mu_assoc_get (prefixes, "");
+ if (pfx)
+ {
+ if (pfx->ns != NS_PRIVATE)
+ {
+ mu_error (_("empty prefix not allowed in the namespace %s"),
+ namespace[pfx->ns].name);
+ exit (EX_CONFIG);
+ }
+ }
+ else
{
- str += strlen (imap4d_homedir);
- if (str[0] == '/')
- str++;
+ struct namespace *priv;
+
+ pfx = mu_zalloc (sizeof (*pfx));
+ pfx->prefix = mu_strdup ("");
+ pfx->dir = mu_strdup ("$home");
+ pfx->delim = '/';
+ priv = namespace_lookup ("private");
+ mu_list_prepend (priv->prefixes, pfx);
+ rc = mu_assoc_install (prefixes, pfx->prefix, pfx);
+ if (rc)
+ {
+ mu_error (_("%s: can't install prefix: %s"),
+ pfx->prefix, mu_strerror (rc));
+ exit (EX_CONFIG);
+ }
}
- return str;
+
+ mu_assoc_sort_r (prefixes, cmplen, NULL);
}
-
+
static int
-print_namespace_fun (void *item, void *data)
+expand_vars (char **env, char const *input, char **output)
{
- int *pcount = data;
- const char *dir = printable_pathname (item);
- char *suf = (dir[0] && dir[strlen (dir) - 1] != '/') ? "/" : "";
- if ((*pcount)++)
- io_sendf (" ");
- io_sendf ("(\"%s%s\" \"/\")", dir, suf);
+ struct mu_wordsplit ws;
+ size_t wordc;
+ char **wordv;
+
+ ws.ws_env = (const char **) env;
+ if (mu_wordsplit (input, &ws,
+ MU_WRDSF_NOSPLIT | MU_WRDSF_NOCMD |
+ MU_WRDSF_ENV | MU_WRDSF_ENV_KV))
+ {
+ mu_error (_("cannot expand line `%s': %s"), input,
+ mu_wordsplit_strerror (&ws));
+ return 1;
+ }
+ mu_wordsplit_get_words (&ws, &wordc, &wordv);
+ *output = wordv[0];
+ mu_wordsplit_free (&ws);
return 0;
}
-static void
-print_namespace (int nsid)
+static char *
+prefix_translate_name (struct namespace_prefix const *pfx, char const *name,
+ size_t namelen, int url)
{
- mu_list_t list = namespace[nsid];
- if (!list)
- io_sendf ("NIL");
- else
+ size_t pfxlen = strlen (pfx->prefix);
+
+ if (pfxlen <= namelen
+ && memcmp (pfx->prefix, name, pfxlen) == 0
+ && (pfxlen == 0 || pfxlen == namelen || name[pfxlen] == pfx->delim))
{
- int count;
- count = 0;
- io_sendf ("(");
- mu_list_foreach (list, print_namespace_fun, &count);
- io_sendf (")");
+ char *tmpl, *p;
+
+ if (!pfx->scheme)
+ url = 0;
+ name += pfxlen;
+
+ if (pfx->ns == NS_PRIVATE && strcmp (name, "INBOX") == 0)
+ {
+ tmpl = mu_strdup (auth_data->mailbox);