From: Timo Sirainen Date: Wed, 26 Sep 2012 15:01:01 +0000 (+0300) Subject: Merged changes from v2.1 tree. X-Git-Tag: 2.2.alpha1~138 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fca7aa88ab19375aba5a8fd9d4100ea885dccb68;p=thirdparty%2Fdovecot%2Fcore.git Merged changes from v2.1 tree. --- fca7aa88ab19375aba5a8fd9d4100ea885dccb68 diff --cc NEWS index a94973ce7b,e4f06b1abe..f10b5ee3e3 --- a/NEWS +++ b/NEWS @@@ -1,24 -1,29 +1,50 @@@ +v2.2.UNSTABLE 2012-xx-xx Timo Sirainen + + * "doveadm auth" command was renamed to "doveadm auth test" + + + Implemented IMAP MOVE and BINARY extensions + + Implemented IMAP CATENATE extension (by Stephan Bosch) + + Implemented IMAP NOTIFY extension. Requires mailbox_list_index=yes + to be enabled. + + Improved mailbox list indexes. They should be usable now, although + still disabled by default. + + Redesigned and rewritten dsync. The new design makes the syncing + faster, more reliable and more featureful. The new dsync protocol + isn't backwards compatible with old dsync versions (but is designed + to be forwards compatible with future versions). + + All mailbox formats now support per-user message flags for shared + mailboxes by using a private index. It can be enabled by adding + :INDEXPVT= to mail location. This should be used instead of + :INDEX also for Maildir/mbox to improve performance. + + LMTP proxy: Implemented XCLIENT extension for passing remote IP + address through proxy. + + v2.1.10 2012-09-18 Timo Sirainen + + + imap: Implemented THREAD=ORDEREDSUBJECT extension. + + Added "doveadm exec" command to easily execute commands from + libexec_dir, e.g. "doveadm exec imap -u user@domain" + + Added "doveadm copy" command. + + doveadm copy/move: Added optional user parameter to specify the + source username. This allows easily copying mails between different + users. + + Added namespace { disabled } setting to quickly enable/disable + namespaces. This is especially useful when its value is returned by + userdb. + + Added mailbox_alias plugin. It allows creating mailbox aliases using + symlinks. + + imapc storage: Added imapc_max_idle_time setting to force activity + on connection. + + fts-solr: Expunging multiple messages is now faster. + - director: In some conditions director may have disconnected from + another director (without logging about it), thinking it was sending + invalid data. + - imap: Various fixes to listing mailboxes. + - pop3-migration plugin: Avoid disconnection from POP3 server due + to idling. + - login processes crashed if there were a lot of local {} or remote {} + settings blocks. + v2.1.9 2012-08-01 Timo Sirainen * mail-log plugin: Log mailbox names with UTF-8 everywhere diff --cc src/auth/auth-request.h index dd74b059e3,0494a25c8c..59d6315e6d --- a/src/auth/auth-request.h +++ b/src/auth/auth-request.h @@@ -210,8 -206,11 +210,12 @@@ int auth_request_password_verify(struc const struct var_expand_table * auth_request_get_var_expand_table(const struct auth_request *auth_request, - auth_request_escape_func_t *escape_func); + auth_request_escape_func_t *escape_func) + ATTR_NULL(2); + struct var_expand_table * + auth_request_get_var_expand_table_full(const struct auth_request *auth_request, + auth_request_escape_func_t *escape_func, - unsigned int *count); ++ unsigned int *count) ATTR_NULL(2); const char *auth_request_str_escape(const char *string, const struct auth_request *request); diff --cc src/auth/db-ldap.c index dd54d9bd58,0c53c53f54..c520a1652a --- a/src/auth/db-ldap.c +++ b/src/auth/db-ldap.c @@@ -61,9 -61,8 +61,8 @@@ struct db_ldap_result_iterate_context const ARRAY_TYPE(ldap_field) *attr_map; unsigned int attr_idx; - /* ldap_attr_name => struct db_ldap_value */ - struct hash_table *ldap_attrs; + /* attribute name => value */ + HASH_TABLE(char *, struct db_ldap_value *) ldap_attrs; - struct var_expand_table *var_table; const char *val_1_arr[2]; string_t *var, *debug; diff --cc src/director/director-connection.c index 5e9104b266,a7b82069c4..e2b13e1977 --- a/src/director/director-connection.c +++ b/src/director/director-connection.c @@@ -183,8 -191,15 +191,14 @@@ static void director_connection_send_co connect_str = t_strdup_printf("CONNECT\t%s\t%u\n", net_ip2addr(&host->ip), host->port); director_connection_send(conn, connect_str); - (void)o_stream_flush(conn->output); o_stream_uncork(conn->output); + /* wait for a while for the remote to disconnect, so it will hopefully + see our CONNECT command. we'll also log the warning later to avoid + multiple log lines about it. */ + conn->connect_request_to = host; + director_host_ref(conn->connect_request_to); + conn->to_disconnect = timeout_add(DIRECTOR_WAIT_DISCONNECT_SECS*1000, director_connection_wait_timeout, conn); diff --cc src/lib-imap/imap-parser.c index 0aeee77421,6fa7c73430..54bc3927a7 --- a/src/lib-imap/imap-parser.c +++ b/src/lib-imap/imap-parser.c @@@ -16,9 -16,9 +16,10 @@@ enum arg_parse_type ARG_PARSE_ATOM, ARG_PARSE_STRING, ARG_PARSE_LITERAL, + ARG_PARSE_LITERAL8, ARG_PARSE_LITERAL_DATA, - ARG_PARSE_LITERAL_DATA_FORCED + ARG_PARSE_LITERAL_DATA_FORCED, + ARG_PARSE_TEXT }; struct imap_parser { diff --cc src/lib-imap/imap-parser.h index 9493707380,357a289ff0..623d3def8b --- a/src/lib-imap/imap-parser.h +++ b/src/lib-imap/imap-parser.h @@@ -17,10 -17,10 +17,14 @@@ enum imap_parser_flags IMAP_PARSE_FLAG_ATOM_ALLCHARS = 0x08, /* Allow strings to contain CRLFs */ IMAP_PARSE_FLAG_MULTILINE_STR = 0x10, + /* Parse in list context; ')' parses as EOL */ + IMAP_PARSE_FLAG_INSIDE_LIST = 0x20, + /* Parse literal8 and set it as flag to imap_arg. */ - IMAP_PARSE_FLAG_LITERAL8 = 0x40 ++ IMAP_PARSE_FLAG_LITERAL8 = 0x40, + /* We're parsing IMAP server replies. Parse the "text" after + OK/NO/BAD/BYE replies as a single atom. We assume that the initial + "*" or tag was already skipped over. */ - IMAP_PARSE_FLAG_SERVER_TEXT = 0x20 ++ IMAP_PARSE_FLAG_SERVER_TEXT = 0x80 }; struct imap_parser; diff --cc src/lib-storage/index/dbox-common/dbox-storage.c index 998c60196d,8090a261e9..1348d0236c --- a/src/lib-storage/index/dbox-common/dbox-storage.c +++ b/src/lib-storage/index/dbox-common/dbox-storage.c @@@ -103,15 -104,11 +103,16 @@@ int dbox_storage_create(struct mail_sto dir = mail_user_home_expand(_storage->user, set->mail_attachment_dir); storage->attachment_dir = p_strdup(_storage->pool, dir); - storage->attachment_fs = fs_init(name, args, &fs_set); - } T_END; + if (fs_init(name, args, &fs_set, &storage->attachment_fs, + &error) < 0) { + *error_r = t_strdup_printf("mail_attachment_fs: %s", + error); + return -1; + } + } - dbox_verify_alt_path(ns->list); + if (!ns->list->set.alt_dir_nocheck) + dbox_verify_alt_path(ns->list); return 0; } diff --cc src/lib-storage/index/dbox-multi/mdbox-file.c index c10ddbba99,6bd96852b4..d150b79b93 --- a/src/lib-storage/index/dbox-multi/mdbox-file.c +++ b/src/lib-storage/index/dbox-multi/mdbox-file.c @@@ -302,11 -302,14 +302,14 @@@ int mdbox_file_create_fd(struct dbox_fi { struct mdbox_file *mfile = (struct mdbox_file *)file; struct mdbox_map *map = mfile->storage->map; - mode_t create_mode, old_mask; - gid_t create_gid; - const char *create_gid_origin, *p, *dir, *error; ++ struct mailbox_permissions perm; + mode_t old_mask; + const char *p, *dir; int fd; - old_mask = umask(0666 & ~map->perm.file_create_mode); - mdbox_map_get_create_mode(map, &create_mode, &create_gid, - &create_gid_origin); - old_mask = umask(0666 & ~create_mode); ++ mailbox_list_get_root_permissions(map->root_list, &perm); ++ ++ old_mask = umask(0666 & ~perm.file_create_mode); fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666); umask(old_mask); if (fd == -1 && errno == ENOENT && parents && @@@ -321,25 -325,25 +324,25 @@@ return -1; } /* try again */ - old_mask = umask(0666 & ~map->perm.file_create_mode); - old_mask = umask(0666 & ~create_mode); ++ old_mask = umask(0666 & ~perm.file_create_mode); fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666); umask(old_mask); } if (fd == -1) { mail_storage_set_critical(&file->storage->storage, "open(%s, O_CREAT) failed: %m", path); - } else if (map->perm.file_create_gid == (gid_t)-1) { - } else if (create_gid == (gid_t)-1) { ++ } else if (perm.file_create_gid == (gid_t)-1) { /* no group change */ - } else if (fchown(fd, (uid_t)-1, map->perm.file_create_gid) < 0) { - } else if (fchown(fd, (uid_t)-1, create_gid) < 0) { ++ } else if (fchown(fd, (uid_t)-1, perm.file_create_gid) < 0) { if (errno == EPERM) { mail_storage_set_critical(&file->storage->storage, "%s", eperm_error_get_chgrp("fchown", path, - map->perm.file_create_gid, - map->perm.file_create_gid_origin)); - create_gid, - create_gid_origin)); ++ perm.file_create_gid, ++ perm.file_create_gid_origin)); } else { mail_storage_set_critical(&file->storage->storage, "fchown(%s, -1, %ld) failed: %m", - path, (long)map->perm.file_create_gid); - path, (long)create_gid); ++ path, (long)perm.file_create_gid); } /* continue anyway */ } diff --cc src/lib-storage/index/dbox-multi/mdbox-map-private.h index 146c22f67e,5d4b7e65ab..261c236ac9 --- a/src/lib-storage/index/dbox-multi/mdbox-map-private.h +++ b/src/lib-storage/index/dbox-multi/mdbox-map-private.h @@@ -20,9 -20,12 +20,8 @@@ struct mdbox_map uint32_t map_ext_id, ref_ext_id; struct mailbox_list *root_list; - struct mailbox_permissions perm; - mode_t _create_mode; - gid_t _create_gid; - char *_create_gid_origin; unsigned int verify_existing_file_ids:1; - unsigned int create_mode_set:1; }; struct mdbox_map_append { diff --cc src/lib-storage/index/dbox-multi/mdbox-map.c index 65f37335bf,7bc20a7b00..8f1d05d2da --- a/src/lib-storage/index/dbox-multi/mdbox-map.c +++ b/src/lib-storage/index/dbox-multi/mdbox-map.c @@@ -140,6 -168,9 +135,7 @@@ static void mdbox_map_cleanup(struct md static int mdbox_map_open_internal(struct mdbox_map *map, bool create_missing) { enum mail_index_open_flags open_flags; - mode_t create_mode; - gid_t create_gid; - const char *create_gid_origin; ++ struct mailbox_permissions perm; int ret = 0; if (map->view != NULL) { @@@ -147,6 -178,11 +143,11 @@@ return 1; } - mdbox_map_get_create_mode(map, &create_mode, &create_gid, - &create_gid_origin); - mail_index_set_permissions(map->index, create_mode, - create_gid, create_gid_origin); ++ mailbox_list_get_root_permissions(map->root_list, &perm); ++ mail_index_set_permissions(map->index, perm.file_create_mode, ++ perm.file_create_gid, ++ perm.file_create_gid_origin); + open_flags = MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY | mail_storage_settings_to_index_flags(MAP_STORAGE(map)->set); if (create_missing) { diff --cc src/lib-storage/list/mailbox-list-fs-iter.c index 7bbf220c8f,7e5e21662b..e45676f182 --- a/src/lib-storage/list/mailbox-list-fs-iter.c +++ b/src/lib-storage/list/mailbox-list-fs-iter.c @@@ -756,11 -777,18 +762,18 @@@ fs_list_iter_next(struct mailbox_list_i if (ret <= 0) return NULL; - if (_ctx->list->ns->type == NAMESPACE_SHARED && ++ if (_ctx->list->ns->type == MAIL_NAMESPACE_TYPE_SHARED && + !_ctx->list->ns->list->mail_set->mail_shared_explicit_inbox && - strlen(ctx->info.name) < _ctx->list->ns->prefix_len) { ++ strlen(ctx->info.vname) < _ctx->list->ns->prefix_len) { + /* shared/user INBOX, IMAP code already lists it */ + return fs_list_iter_next(_ctx); + } + if ((ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_SUBSCRIBED) != 0) { mailbox_list_set_subscription_flags(ctx->ctx.list, - ctx->info.name, + ctx->info.vname, &ctx->info.flags); } - i_assert(ctx->info.name != NULL); + i_assert(ctx->info.vname != NULL); return &ctx->info; } diff --cc src/lib-storage/mailbox-list-iter.h index 8df0bc77b0,0000000000..a0c1597aaa mode 100644,000000..100644 --- a/src/lib-storage/mailbox-list-iter.h +++ b/src/lib-storage/mailbox-list-iter.h @@@ -1,85 -1,0 +1,87 @@@ +#ifndef MAILBOX_LIST_ITER_H +#define MAILBOX_LIST_ITER_H + +#include "mail-namespace.h" +#include "mailbox-list.h" + +enum mailbox_list_iter_flags { + /* Ignore index file and ACLs (used by ACL plugin internally) */ + MAILBOX_LIST_ITER_RAW_LIST = 0x000001, + /* Don't list autocreated mailboxes (e.g. INBOX) unless they + physically exist */ + MAILBOX_LIST_ITER_NO_AUTO_BOXES = 0x000004, + - /* For mailbox_list_iter_init_namespaces(): Skip namespaces that ++ /* Skip all kinds of mailbox aliases. This typically includes symlinks ++ that point to the same directory. Also when iterating with ++ mailbox_list_iter_init_namespaces() skip namespaces that + have alias_for set. */ + MAILBOX_LIST_ITER_SKIP_ALIASES = 0x000008, + /* For mailbox_list_iter_init_namespaces(): '*' in a pattern doesn't + match beyond namespace boundary (e.g. "foo*" or "*o" doesn't match + "foo." namespace's mailboxes, but "*.*" does). also '%' can't match + namespace prefixes, if there exists a parent namespace whose children + it matches. */ + MAILBOX_LIST_ITER_STAR_WITHIN_NS = 0x000010, + + /* List only subscribed mailboxes */ + MAILBOX_LIST_ITER_SELECT_SUBSCRIBED = 0x000100, + /* Return MAILBOX_CHILD_* if mailbox's children match selection + criteria, even if the mailbox itself wouldn't match. */ + MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH = 0x000200, + /* Return only mailboxes that have special use flags */ + MAILBOX_LIST_ITER_SELECT_SPECIALUSE = 0x000400, + + /* Don't return any flags unless it can be done without cost */ + MAILBOX_LIST_ITER_RETURN_NO_FLAGS = 0x001000, + /* Return MAILBOX_SUBSCRIBED flag */ + MAILBOX_LIST_ITER_RETURN_SUBSCRIBED = 0x002000, + /* Return children flags */ + MAILBOX_LIST_ITER_RETURN_CHILDREN = 0x004000, + /* Return IMAP special use flags */ + MAILBOX_LIST_ITER_RETURN_SPECIALUSE = 0x008000 +}; + +struct mailbox_info { + const char *vname; + const char *special_use; + enum mailbox_info_flags flags; + + struct mail_namespace *ns; +}; + +/* Returns a single pattern from given reference and pattern. */ +const char *mailbox_list_join_refpattern(struct mailbox_list *list, + const char *ref, const char *pattern); + +/* Initialize new mailbox list request. Pattern may contain '%' and '*' + wildcards as defined by RFC-3501. */ +struct mailbox_list_iterate_context * +mailbox_list_iter_init(struct mailbox_list *list, const char *pattern, + enum mailbox_list_iter_flags flags); +/* Like mailbox_list_iter_init(), but support multiple patterns. Patterns is + a NULL-terminated list of strings. It must contain at least one pattern. */ +struct mailbox_list_iterate_context * +mailbox_list_iter_init_multiple(struct mailbox_list *list, + const char *const *patterns, + enum mailbox_list_iter_flags flags); +/* List mailbox_list_iter_init_multiple(), but list mailboxes from all the + specified namespaces. */ +struct mailbox_list_iterate_context * +mailbox_list_iter_init_namespaces(struct mail_namespace *namespaces, + const char *const *patterns, + enum mail_namespace_type type_mask, + enum mailbox_list_iter_flags flags); +/* Get next mailbox. Returns the mailbox name */ +const struct mailbox_info * +mailbox_list_iter_next(struct mailbox_list_iterate_context *ctx); +/* Deinitialize mailbox list request. Returns -1 if some error + occurred while listing. */ +int mailbox_list_iter_deinit(struct mailbox_list_iterate_context **ctx); +/* List one mailbox. Returns 1 if info returned, 0 if mailbox doesn't exist, + -1 if error. */ +int mailbox_list_mailbox(struct mailbox_list *list, const char *name, + enum mailbox_info_flags *flags_r); +/* Returns 1 if mailbox has children, 0 if not, -1 if error. */ +int mailbox_has_children(struct mailbox_list *list, const char *name); + +#endif diff --cc src/lib-storage/mailbox-list-private.h index aead168c40,5c5a2922e0..813450d29b --- a/src/lib-storage/mailbox-list-private.h +++ b/src/lib-storage/mailbox-list-private.h @@@ -184,8 -192,13 +184,11 @@@ int mailbox_list_iter_subscriptions_ref bool mailbox_list_name_is_too_large(const char *name, char sep); enum mailbox_list_file_type mailbox_list_get_file_type(const struct dirent *d); + int mailbox_list_dirent_is_alias_symlink(struct mailbox_list *list, + const char *dir_path, + const struct dirent *d); bool mailbox_list_try_get_absolute_path(struct mailbox_list *list, const char **name); -int mailbox_list_create_missing_index_dir(struct mailbox_list *list, - const char *name); void mailbox_list_add_change(struct mailbox_list *list, enum mailbox_log_record_type type, diff --cc src/lib-storage/mailbox-list.h index d6c9086f0f,1d0c61cd07..1dbda64dbe --- a/src/lib-storage/mailbox-list.h +++ b/src/lib-storage/mailbox-list.h @@@ -105,8 -145,18 +105,10 @@@ struct mailbox_list_settings char escape_char; /* Use UTF-8 mailbox names on filesystem instead of mUTF-7 */ bool utf8; + /* Don't check/create the alt-dir symlink. */ + bool alt_dir_nocheck; }; -struct mailbox_info { - const char *name; - const char *special_use; - enum mailbox_info_flags flags; - - struct mail_namespace *ns; -}; - struct mailbox_permissions { /* The actual uid/gid of the mailbox */ uid_t file_uid; diff --cc src/lib/hash.c index 816b8b9ab3,7c52b7cbd7..fbd45c9469 --- a/src/lib/hash.c +++ b/src/lib/hash.c @@@ -8,24 -8,8 +8,24 @@@ #include - #define HASH_TABLE_MIN_SIZE 131 + #define HASH_TABLE_MIN_SIZE 67 +#undef hash_table_create +#undef hash_table_create_direct +#undef hash_table_destroy +#undef hash_table_clear +#undef hash_table_lookup +#undef hash_table_lookup_full +#undef hash_table_insert +#undef hash_table_update +#undef hash_table_remove +#undef hash_table_count +#undef hash_table_iterate_init +#undef hash_table_iterate +#undef hash_table_freeze +#undef hash_table_thaw +#undef hash_table_copy + struct hash_node { struct hash_node *next; void *key; diff --cc src/lmtp/lmtp-settings.c index eb4400c6cf,b7e3ae6c48..c699ed81df --- a/src/lmtp/lmtp-settings.c +++ b/src/lmtp/lmtp-settings.c @@@ -59,9 -59,9 +59,10 @@@ struct service_settings lmtp_service_se static const struct setting_define lmtp_setting_defines[] = { DEF(SET_BOOL, lmtp_proxy), DEF(SET_BOOL, lmtp_save_to_detail_mailbox), + DEF(SET_BOOL, lmtp_rcpt_check_quota), - DEF(SET_STR_VARS, login_greeting), DEF(SET_STR, lmtp_address_translate), + DEF(SET_STR_VARS, login_greeting), + DEF(SET_STR, login_trusted_networks), SETTING_DEFINE_LIST_END }; @@@ -69,9 -69,9 +70,10 @@@ static const struct lmtp_settings lmtp_default_settings = { .lmtp_proxy = FALSE, .lmtp_save_to_detail_mailbox = FALSE, + .lmtp_rcpt_check_quota = FALSE, + .lmtp_address_translate = "", .login_greeting = PACKAGE_NAME" ready.", - .lmtp_address_translate = "" + .login_trusted_networks = "" }; static const struct setting_parser_info *lmtp_setting_dependencies[] = { diff --cc src/lmtp/lmtp-settings.h index 57178484d1,92d8c4f5bd..b01a4740c4 --- a/src/lmtp/lmtp-settings.h +++ b/src/lmtp/lmtp-settings.h @@@ -7,9 -7,9 +7,10 @@@ struct lmtp_settings struct lmtp_settings { bool lmtp_proxy; bool lmtp_save_to_detail_mailbox; + bool lmtp_rcpt_check_quota; - const char *login_greeting; const char *lmtp_address_translate; + const char *login_greeting; + const char *login_trusted_networks; }; extern const struct setting_parser_info lmtp_setting_parser_info; diff --cc src/plugins/mailbox-alias/mailbox-alias-plugin.c index 0000000000,86e60f15d6..43e4b46f48 mode 000000,100644..100644 --- a/src/plugins/mailbox-alias/mailbox-alias-plugin.c +++ b/src/plugins/mailbox-alias/mailbox-alias-plugin.c @@@ -1,0 -1,335 +1,334 @@@ + /* Copyright (c) 2012 Dovecot authors, see the included COPYING file */ + + #include "lib.h" + #include "str.h" + #include "mail-storage-hooks.h" + #include "mail-storage-private.h" + #include "mailbox-list-private.h" + #include "mailbox-alias-plugin.h" + + #define MAILBOX_ALIAS_USER_CONTEXT(obj) \ + MODULE_CONTEXT(obj, mailbox_alias_user_module) + #define MAILBOX_ALIAS_CONTEXT(obj) \ + MODULE_CONTEXT(obj, mailbox_alias_storage_module) + #define MAILBOX_ALIAS_LIST_CONTEXT(obj) \ + MODULE_CONTEXT(obj, mailbox_alias_mailbox_list_module) + + struct mailbox_alias { + const char *old_vname, *new_vname; + }; + + struct mailbox_alias_user { + union mail_user_module_context module_ctx; + - ARRAY_DEFINE(aliases, struct mailbox_alias); ++ ARRAY(struct mailbox_alias) aliases; + }; + + struct mailbox_alias_mailbox_list { + union mailbox_list_module_context module_ctx; + }; + + struct mailbox_alias_mailbox { + union mailbox_module_context module_ctx; + }; + + enum mailbox_symlink_existence { + MAILBOX_SYMLINK_EXISTENCE_NONEXISTENT, + MAILBOX_SYMLINK_EXISTENCE_SYMLINK, + MAILBOX_SYMLINK_EXISTENCE_NOT_SYMLINK + }; + + static MODULE_CONTEXT_DEFINE_INIT(mailbox_alias_user_module, + &mail_user_module_register); + static MODULE_CONTEXT_DEFINE_INIT(mailbox_alias_storage_module, + &mail_storage_module_register); + static MODULE_CONTEXT_DEFINE_INIT(mailbox_alias_mailbox_list_module, + &mailbox_list_module_register); + + const char *mailbox_alias_plugin_version = DOVECOT_VERSION; + + static const char * + mailbox_alias_find_new(struct mail_user *user, const char *new_vname) + { + struct mailbox_alias_user *auser = MAILBOX_ALIAS_USER_CONTEXT(user); + const struct mailbox_alias *alias; + + array_foreach(&auser->aliases, alias) { + if (strcmp(alias->new_vname, new_vname) == 0) + return alias->old_vname; + } + return NULL; + } + + static int mailbox_symlink_exists(struct mailbox_list *list, const char *vname, + enum mailbox_symlink_existence *existence_r) + { + struct mailbox_alias_mailbox_list *alist = + MAILBOX_ALIAS_LIST_CONTEXT(list); + struct stat st; + const char *symlink_name, *symlink_path; + + symlink_name = alist->module_ctx.super.get_storage_name(list, vname); + symlink_path = mailbox_list_get_path(list, symlink_name, + MAILBOX_LIST_PATH_TYPE_DIR); + if (lstat(symlink_path, &st) < 0) { + if (errno == ENOENT) { + *existence_r = MAILBOX_SYMLINK_EXISTENCE_NONEXISTENT; + return 0; + } + mailbox_list_set_critical(list, + "lstat(%s) failed: %m", symlink_path); + return -1; + } + if (S_ISLNK(st.st_mode)) + *existence_r = MAILBOX_SYMLINK_EXISTENCE_SYMLINK; + else + *existence_r = MAILBOX_SYMLINK_EXISTENCE_NOT_SYMLINK; + return 0; + } + + static int mailbox_is_alias_symlink(struct mailbox *box) + { + enum mailbox_symlink_existence existence; + + if (mailbox_alias_find_new(box->storage->user, box->vname) == NULL) + return 0; + if (mailbox_symlink_exists(box->list, box->vname, &existence) < 0) { + mail_storage_copy_list_error(box->storage, box->list); + return -1; + } + return existence == MAILBOX_SYMLINK_EXISTENCE_SYMLINK ? 1 : 0; + } + + static int + mailbox_has_aliases(struct mailbox_list *list, const char *old_vname) + { + struct mailbox_alias_user *auser = + MAILBOX_ALIAS_USER_CONTEXT(list->ns->user); + const struct mailbox_alias *alias; + enum mailbox_symlink_existence existence; + int ret = 0; + + array_foreach(&auser->aliases, alias) { + if (strcmp(alias->old_vname, old_vname) == 0) { + if (mailbox_symlink_exists(list, alias->new_vname, + &existence) < 0) + ret = -1; + if (existence == MAILBOX_SYMLINK_EXISTENCE_SYMLINK) + return 1; + } + } + return ret; + } + + static int + mailbox_alias_create_symlink(struct mailbox *box, + const char *old_name, const char *new_name) + { + const char *old_path, *new_path, *fname; + + old_path = mailbox_list_get_path(box->list, old_name, + MAILBOX_LIST_PATH_TYPE_DIR); + new_path = mailbox_list_get_path(box->list, new_name, + MAILBOX_LIST_PATH_TYPE_DIR); + fname = strrchr(old_path, '/'); + i_assert(fname != NULL); + fname++; + i_assert(strncmp(new_path, old_path, fname-old_path) == 0); + + if (symlink(fname, new_path) < 0) { + if (errno == EEXIST) { + mail_storage_set_error(box->storage, MAIL_ERROR_EXISTS, + "Mailbox already exists"); + return -1; + } + mail_storage_set_critical(box->storage, + "symlink(%s, %s) failed: %m", fname, new_path); + return -1; + } + return 0; + } + + static const char * + mailbox_alias_get_storage_name(struct mailbox_list *list, const char *vname) + { + struct mailbox_alias_mailbox_list *alist = + MAILBOX_ALIAS_LIST_CONTEXT(list); + const char *old_vname; + enum mailbox_symlink_existence existence; + + /* access the old mailbox so that e.g. full text search won't + index the mailbox twice. this also means that deletion must be + careful to delete the symlink, box->name. */ + old_vname = mailbox_alias_find_new(list->ns->user, vname); + if (old_vname != NULL && + mailbox_symlink_exists(list, vname, &existence) == 0 && + existence != MAILBOX_SYMLINK_EXISTENCE_NOT_SYMLINK) + vname = old_vname; + + return alist->module_ctx.super.get_storage_name(list, vname); + } + + static int + mailbox_alias_create(struct mailbox *box, const struct mailbox_update *update, + bool directory) + { + struct mailbox_alias_mailbox *abox = MAILBOX_ALIAS_CONTEXT(box); + struct mailbox_alias_mailbox_list *alist = + MAILBOX_ALIAS_LIST_CONTEXT(box->list); + const char *symlink_name; + int ret; + - ret = abox->module_ctx.super.create(box, update, directory); ++ ret = abox->module_ctx.super.create_box(box, update, directory); + if (mailbox_alias_find_new(box->storage->user, box->vname) == NULL) + return ret; + if (ret < 0 && mailbox_get_last_mail_error(box) != MAIL_ERROR_EXISTS) + return ret; + + /* all the code so far has actually only created the original + mailbox. now we'll create the symlink if it's missing. */ + symlink_name = alist->module_ctx.super. + get_storage_name(box->list, box->vname); + return mailbox_alias_create_symlink(box, box->name, symlink_name); + } + + static int mailbox_alias_delete(struct mailbox *box) + { + struct mailbox_alias_mailbox *abox = MAILBOX_ALIAS_CONTEXT(box); + struct mailbox_alias_mailbox_list *alist = + MAILBOX_ALIAS_LIST_CONTEXT(box->list); + const char *symlink_name; + int ret; + + ret = mailbox_has_aliases(box->list, box->vname); + if (ret < 0) + return -1; + if (ret > 0) { + mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE, + "Can't delete mailbox while it has aliases"); + return -1; + } + + if (mailbox_is_alias_symlink(box)) { + /* we're deleting an alias mailbox. we'll need to handle this + explicitly since box->name points to the original mailbox */ + symlink_name = alist->module_ctx.super. + get_storage_name(box->list, box->vname); + if (mailbox_list_delete_symlink(box->list, symlink_name) < 0) { + mail_storage_copy_list_error(box->storage, box->list); + return -1; + } + return 0; + } + - return abox->module_ctx.super.delete(box); ++ return abox->module_ctx.super.delete_box(box); + } + -static int mailbox_alias_rename(struct mailbox *src, struct mailbox *dest, - bool rename_children) ++static int mailbox_alias_rename(struct mailbox *src, struct mailbox *dest) + { + struct mailbox_alias_mailbox *abox = MAILBOX_ALIAS_CONTEXT(src); + int ret; + + if (mailbox_is_alias_symlink(src)) { + mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE, + "Can't rename alias mailboxes"); + return -1; + } + if (mailbox_is_alias_symlink(dest)) { + mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE, + "Can't rename to mailbox alias"); + return -1; + } + ret = mailbox_has_aliases(src->list, src->vname); + if (ret < 0) + return -1; + if (ret > 0) { + mail_storage_set_error(src->storage, MAIL_ERROR_NOTPOSSIBLE, + "Can't rename mailbox while it has aliases"); + return -1; + } + - return abox->module_ctx.super.rename(src, dest, rename_children); ++ return abox->module_ctx.super.rename_box(src, dest); + } + + static void mailbox_alias_mail_user_created(struct mail_user *user) + { + struct mail_user_vfuncs *v = user->vlast; + struct mailbox_alias_user *auser; + struct mailbox_alias *alias; + string_t *oldkey, *newkey; + const char *old_vname, *new_vname; + unsigned int i; + + auser = p_new(user->pool, struct mailbox_alias_user, 1); + auser->module_ctx.super = *v; + user->vlast = &auser->module_ctx.super; + + p_array_init(&auser->aliases, user->pool, 8); + + oldkey = t_str_new(32); + newkey = t_str_new(32); + str_append(oldkey, "mailbox_alias_old"); + str_append(newkey, "mailbox_alias_new"); + for (i = 2;; i++) { + old_vname = mail_user_plugin_getenv(user, str_c(oldkey)); + new_vname = mail_user_plugin_getenv(user, str_c(newkey)); + if (old_vname == NULL || new_vname == NULL) + break; + + alias = array_append_space(&auser->aliases); + alias->old_vname = old_vname; + alias->new_vname = new_vname; + + str_truncate(oldkey, 0); + str_truncate(newkey, 0); + str_printfa(oldkey, "mailbox_alias_old%u", i); + str_printfa(newkey, "mailbox_alias_new%u", i); + } + + MODULE_CONTEXT_SET(user, mailbox_alias_user_module, auser); + } + + static void mailbox_alias_mailbox_list_created(struct mailbox_list *list) + { + struct mailbox_list_vfuncs *v = list->vlast; + struct mailbox_alias_mailbox_list *alist; + + alist = p_new(list->pool, struct mailbox_alias_mailbox_list, 1); + alist->module_ctx.super = *v; + list->vlast = &alist->module_ctx.super; + + v->get_storage_name = mailbox_alias_get_storage_name; + MODULE_CONTEXT_SET(list, mailbox_alias_mailbox_list_module, alist); + } + + static void mailbox_alias_mailbox_allocated(struct mailbox *box) + { + struct mailbox_vfuncs *v = box->vlast; + struct mailbox_alias_mailbox *abox; + + abox = p_new(box->pool, struct mailbox_alias_mailbox, 1); + abox->module_ctx.super = *v; + box->vlast = &abox->module_ctx.super; + - v->create = mailbox_alias_create; - v->delete = mailbox_alias_delete; - v->rename = mailbox_alias_rename; ++ v->create_box = mailbox_alias_create; ++ v->delete_box = mailbox_alias_delete; ++ v->rename_box = mailbox_alias_rename; + MODULE_CONTEXT_SET(box, mailbox_alias_storage_module, abox); + } + + static struct mail_storage_hooks mailbox_alias_mail_storage_hooks = { + .mail_user_created = mailbox_alias_mail_user_created, + .mailbox_list_created = mailbox_alias_mailbox_list_created, + .mailbox_allocated = mailbox_alias_mailbox_allocated + }; + + void mailbox_alias_plugin_init(struct module *module) + { + mail_storage_hooks_add(module, &mailbox_alias_mail_storage_hooks); + } + + void mailbox_alias_plugin_deinit(void) + { + mail_storage_hooks_remove(&mailbox_alias_mail_storage_hooks); + }