diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2017-01-04 10:54:18 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2017-01-04 13:56:32 +0200 |
commit | 9a94d41a0c8cd941db019478c7a75b1036ddf580 (patch) | |
tree | 0487aacf4e00e6dbe9118eb926125c5b370d356b | |
parent | 068c059972fbc53d734ab816d7583ec2fb918b12 (diff) | |
download | mailutils-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.c | 2 | ||||
-rw-r--r-- | imap4d/copy.c | 6 | ||||
-rw-r--r-- | imap4d/create.c | 6 | ||||
-rw-r--r-- | imap4d/delete.c | 2 | ||||
-rw-r--r-- | imap4d/imap4d.c | 239 | ||||
-rw-r--r-- | imap4d/imap4d.h | 40 | ||||
-rw-r--r-- | imap4d/list.c | 131 | ||||
-rw-r--r-- | imap4d/namespace.c | 576 | ||||
-rw-r--r-- | imap4d/quota.c | 6 | ||||
-rw-r--r-- | imap4d/rename.c | 9 | ||||
-rw-r--r-- | imap4d/select.c | 2 | ||||
-rw-r--r-- | imap4d/status.c | 2 | ||||
-rw-r--r-- | imap4d/tests/IDEF0955.at | 6 | ||||
-rw-r--r-- | imap4d/tests/IDEF0956.at | 8 | ||||
-rw-r--r-- | imap4d/tests/append00.at | 9 | ||||
-rw-r--r-- | imap4d/tests/append01.at | 9 | ||||
-rw-r--r-- | imap4d/tests/atlocal.in | 29 | ||||
-rw-r--r-- | imap4d/tests/create01.at | 5 | ||||
-rw-r--r-- | imap4d/tests/create02.at | 5 | ||||
-rw-r--r-- | imap4d/tests/list.at | 54 | ||||
-rw-r--r-- | imap4d/tests/testsuite.at | 30 | ||||
-rw-r--r-- | imap4d/util.c | 20 |
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", §ion)) + 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", §ion)) + 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); |