static const char * get_parent_mailbox(struct acl_backend *backend, const char *name) { const char *p; p = strrchr(name, mailbox_list_get_hierarchy_sep(backend->list)); return p == NULL ? NULL : t_strdup_until(name, p); }
static void mailbox_list_index_update_info(struct mailbox_list_index_iterate_context *ctx) { struct mailbox_list_index_node *node = ctx->next_node; struct mailbox *box; p_clear(ctx->info_pool); str_truncate(ctx->path, ctx->parent_len); /* the root directory may have an empty name. in that case we'll still want to insert the separator, so check for non-NULL parent rather than non-empty path. */ if (node->parent != NULL) { str_append_c(ctx->path, mailbox_list_get_hierarchy_sep(ctx->ctx.list)); } str_append(ctx->path, node->name); ctx->info.vname = mailbox_list_get_vname(ctx->ctx.list, str_c(ctx->path)); ctx->info.flags = node->children != NULL ? MAILBOX_CHILDREN : MAILBOX_NOCHILDREN; if (strcmp(ctx->info.vname, "INBOX") != 0) { /* non-INBOX */ ctx->info.vname = p_strdup(ctx->info_pool, ctx->info.vname); } else if (!ctx->prefix_inbox_list) { /* listing INBOX itself */ ctx->info.vname = "INBOX"; if (mail_namespace_is_inbox_noinferiors(ctx->info.ns)) { ctx->info.flags &= ~(MAILBOX_CHILDREN|MAILBOX_NOCHILDREN); ctx->info.flags |= MAILBOX_NOINFERIORS; } } else { /* listing INBOX/INBOX */ ctx->info.vname = p_strconcat(ctx->info_pool, ctx->ctx.list->ns->prefix, "INBOX", NULL); ctx->info.flags |= MAILBOX_NONEXISTENT; } if ((node->flags & MAILBOX_LIST_INDEX_FLAG_NONEXISTENT) != 0) ctx->info.flags |= MAILBOX_NONEXISTENT; else if ((node->flags & MAILBOX_LIST_INDEX_FLAG_NOSELECT) != 0) ctx->info.flags |= MAILBOX_NOSELECT; if ((node->flags & MAILBOX_LIST_INDEX_FLAG_NOINFERIORS) != 0) ctx->info.flags |= MAILBOX_NOINFERIORS; if ((ctx->ctx.flags & (MAILBOX_LIST_ITER_SELECT_SUBSCRIBED | MAILBOX_LIST_ITER_RETURN_SUBSCRIBED)) != 0) { mailbox_list_set_subscription_flags(ctx->ctx.list, ctx->info.vname, &ctx->info.flags); } if ((ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) == 0) { box = mailbox_alloc(ctx->ctx.list, ctx->info.vname, 0); mailbox_list_index_status_set_info_flags(box, node->uid, &ctx->info.flags); mailbox_free(&box); } }
static void dsync_fix_mailbox_name(struct mail_namespace *ns, string_t *vname, char alt_char) { const char *old_vname; char *p, list_sep = mailbox_list_get_hierarchy_sep(ns->list); guid_128_t guid; /* replace control chars */ for (p = str_c_modifiable(vname); *p != '\0'; p++) { if ((unsigned char)*p < ' ') *p = alt_char; } /* make it valid UTF8 */ if (!uni_utf8_str_is_valid(str_c(vname))) { old_vname = t_strdup(str_c(vname)); str_truncate(vname, 0); if (uni_utf8_get_valid_data((const void *)old_vname, strlen(old_vname), vname)) i_unreached(); } if (dsync_is_valid_name(ns, str_c(vname))) return; /* 1) change any real separators to alt separators (this wouldn't be necessary with listescape, but don't bother detecting it) */ if (list_sep != mail_namespace_get_sep(ns)) { for (p = str_c_modifiable(vname); *p != '\0'; p++) { if (*p == list_sep) *p = alt_char; } if (dsync_is_valid_name(ns, str_c(vname))) return; } /* 2) '/' characters aren't valid without listescape */ if (mail_namespace_get_sep(ns) != '/' && list_sep != '/') { for (p = str_c_modifiable(vname); *p != '\0'; p++) { if (*p == '/') *p = alt_char; } if (dsync_is_valid_name(ns, str_c(vname))) return; } /* 3) probably some reserved name (e.g. dbox-Mails) */ str_insert(vname, ns->prefix_len, "_"); if (dsync_is_valid_name(ns, str_c(vname))) return; /* 4) name is too long? just give up and generate a unique name */ guid_128_generate(guid); str_truncate(vname, 0); str_append(vname, ns->prefix); str_append(vname, guid_128_to_string(guid)); i_assert(dsync_is_valid_name(ns, str_c(vname))); }
static const char * maildir_list_get_dirname_path(struct mailbox_list *list, const char *dir, const char *name) { if (*name == '\0') return dir; else if (list->name == imapdir_mailbox_list.name) return t_strdup_printf("%s/%s", dir, name); return t_strdup_printf("%s/%c%s", dir, mailbox_list_get_hierarchy_sep(list), name); }
static const char * subsfile_list_unescaped(struct subsfile_list_context *ctx, const char *line) { const char *p; str_truncate(ctx->name, 0); while ((p = strchr(line, '\t')) != NULL) { str_append_tabunescaped(ctx->name, line, p-line); str_append_c(ctx->name, mailbox_list_get_hierarchy_sep(ctx->list)); line = p+1; } str_append_tabunescaped(ctx->name, line, strlen(line)); return str_c(ctx->name); }
static const char * imapc_list_to_vname(struct imapc_mailbox_list *list, const char *imapc_name) { const char *list_name; /* typically mailbox_list_escape_name() is used to escape vname into a list name. but we want to convert remote IMAP name to a list name, so we need to use the remote IMAP separator. */ list_name = mailbox_list_escape_name_params(imapc_name, "", list->root_sep, mailbox_list_get_hierarchy_sep(&list->list), list->list.set.escape_char, ""); /* list_name is now valid, so we can convert it to vname */ return mailbox_list_get_vname(&list->list, list_name); }
static const char * imapc_list_get_storage_name(struct mailbox_list *_list, const char *vname) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; const char *prefix = list->set->imapc_list_prefix; const char *storage_name; storage_name = mailbox_list_default_get_storage_name(_list, vname); if (*prefix != '\0' && strcasecmp(storage_name, "INBOX") != 0) { storage_name = storage_name[0] == '\0' ? prefix : t_strdup_printf("%s%c%s", prefix, mailbox_list_get_hierarchy_sep(_list), storage_name); } return storage_name; }
static const char * acl_mailbox_list_iter_get_name(struct mailbox_list_iterate_context *ctx, const char *vname) { struct mail_namespace *ns = ctx->list->ns; const char *name; unsigned int len; name = mailbox_list_get_storage_name(ns->list, vname); len = strlen(name); if (len > 0 && name[len-1] == mailbox_list_get_hierarchy_sep(ns->list)) { /* name ends with separator. this can happen if doing e.g. LIST "" foo/% and it lists "foo/". */ name = t_strndup(name, len-1); } return name; }
static struct mailbox_list_index_node * mailbox_list_index_lookup_real(struct mailbox_list *list, const char *name) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); struct mailbox_list_index_node *node = ilist->mailbox_tree; const char *const *path; unsigned int i; char sep[2]; if (*name == '\0') return mailbox_list_index_node_find_sibling(node, ""); sep[0] = mailbox_list_get_hierarchy_sep(list); sep[1] = '\0'; path = t_strsplit(name, sep); for (i = 0;; i++) { node = mailbox_list_index_node_find_sibling(node, path[i]); if (node == NULL || path[i+1] == NULL) break; node = node->children; } return node; }
static void mailbox_list_index_update_info(struct mailbox_list_index_iterate_context *ctx) { struct mailbox_list_index_node *node = ctx->next_node; struct mailbox *box; p_clear(ctx->info_pool); str_truncate(ctx->path, ctx->parent_len); if (str_len(ctx->path) > 0) { str_append_c(ctx->path, mailbox_list_get_hierarchy_sep(ctx->ctx.list)); } str_append(ctx->path, node->name); ctx->info.vname = mailbox_list_get_vname(ctx->ctx.list, str_c(ctx->path)); ctx->info.vname = p_strdup(ctx->info_pool, ctx->info.vname); ctx->info.flags = 0; if ((node->flags & MAILBOX_LIST_INDEX_FLAG_NONEXISTENT) != 0) ctx->info.flags |= MAILBOX_NONEXISTENT; else if ((node->flags & MAILBOX_LIST_INDEX_FLAG_NOSELECT) != 0) ctx->info.flags |= MAILBOX_NOSELECT; if ((node->flags & MAILBOX_LIST_INDEX_FLAG_NOINFERIORS) != 0) ctx->info.flags |= MAILBOX_NOINFERIORS; ctx->info.flags |= node->children != NULL ? MAILBOX_CHILDREN : MAILBOX_NOCHILDREN; if ((ctx->ctx.flags & (MAILBOX_LIST_ITER_SELECT_SUBSCRIBED | MAILBOX_LIST_ITER_RETURN_SUBSCRIBED)) != 0) { mailbox_list_set_subscription_flags(ctx->ctx.list, ctx->info.vname, &ctx->info.flags); } box = mailbox_alloc(ctx->ctx.list, ctx->info.vname, 0); mailbox_list_index_status_set_info_flags(box, node->uid, &ctx->info.flags); mailbox_free(&box); }
static const char * imapc_list_get_vname(struct mailbox_list *_list, const char *storage_name) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; const char *prefix = list->set->imapc_list_prefix; unsigned int prefix_len; if (*storage_name == '\0') { /* ACL plugin does these lookups */ } else if (*prefix != '\0' && strcasecmp(storage_name, "INBOX") != 0) { prefix_len = strlen(prefix); i_assert(strncmp(prefix, storage_name, prefix_len) == 0); storage_name += prefix_len; if (storage_name[0] == '\0') { /* we're looking up the prefix itself */ } else { i_assert(storage_name[0] == mailbox_list_get_hierarchy_sep(_list)); storage_name++; } } return mailbox_list_default_get_vname(_list, storage_name); }
int subsfile_set_subscribed(struct mailbox_list *list, const char *path, const char *temp_prefix, const char *name, bool set) { const struct mail_storage_settings *mail_set = list->mail_set; struct dotlock_settings dotlock_set; struct dotlock *dotlock; struct mailbox_permissions perm; const char *line, *dir, *fname, *escaped_name; struct istream *input = NULL; struct ostream *output; int fd_in, fd_out; enum mailbox_list_path_type type; bool found, changed = FALSE, failed = FALSE; unsigned int version = 2; if (strcasecmp(name, "INBOX") == 0) name = "INBOX"; memset(&dotlock_set, 0, sizeof(dotlock_set)); dotlock_set.use_excl_lock = mail_set->dotlock_use_excl; dotlock_set.nfs_flush = mail_set->mail_nfs_storage; dotlock_set.temp_prefix = temp_prefix; dotlock_set.timeout = SUBSCRIPTION_FILE_LOCK_TIMEOUT; dotlock_set.stale_timeout = SUBSCRIPTION_FILE_CHANGE_TIMEOUT; mailbox_list_get_root_permissions(list, &perm); fd_out = file_dotlock_open_group(&dotlock_set, path, 0, perm.file_create_mode, perm.file_create_gid, perm.file_create_gid_origin, &dotlock); if (fd_out == -1 && errno == ENOENT) { /* directory hasn't been created yet. */ type = list->set.control_dir != NULL ? MAILBOX_LIST_PATH_TYPE_CONTROL : MAILBOX_LIST_PATH_TYPE_DIR; fname = strrchr(path, '/'); if (fname != NULL) { dir = t_strdup_until(path, fname); if (mailbox_list_mkdir_root(list, dir, type) < 0) return -1; } fd_out = file_dotlock_open_group(&dotlock_set, path, 0, perm.file_create_mode, perm.file_create_gid, perm.file_create_gid_origin, &dotlock); } if (fd_out == -1) { if (errno == EAGAIN) { mailbox_list_set_error(list, MAIL_ERROR_TEMP, "Timeout waiting for subscription file lock"); } else { subswrite_set_syscall_error(list, "file_dotlock_open()", path); } return -1; } fd_in = nfs_safe_open(path, O_RDONLY); if (fd_in == -1 && errno != ENOENT) { subswrite_set_syscall_error(list, "open()", path); file_dotlock_delete(&dotlock); return -1; } if (fd_in != -1) { input = i_stream_create_fd_autoclose(&fd_in, list->mailbox_name_max_length+1); i_stream_set_return_partial_line(input, TRUE); subsfile_list_read_header(list, input, &version); } found = FALSE; output = o_stream_create_fd_file(fd_out, 0, FALSE); o_stream_cork(output); if (version >= 2) o_stream_send_str(output, version2_header); if (version < 2 || name[0] == '\0') escaped_name = name; else { const char *const *tmp; char separators[2]; string_t *str = t_str_new(64); separators[0] = mailbox_list_get_hierarchy_sep(list); separators[1] = '\0'; tmp = t_strsplit(name, separators); str_append_tabescaped(str, *tmp); for (tmp++; *tmp != NULL; tmp++) { str_append_c(str, '\t'); str_append_tabescaped(str, *tmp); } escaped_name = str_c(str); } if (input != NULL) { while ((line = next_line(list, path, input, &failed, FALSE)) != NULL) { if (strcmp(line, escaped_name) == 0) { found = TRUE; if (!set) { changed = TRUE; continue; } } o_stream_nsend_str(output, line); o_stream_nsend(output, "\n", 1); } i_stream_destroy(&input); } if (!failed && set && !found) { /* append subscription */ line = t_strconcat(escaped_name, "\n", NULL); o_stream_nsend_str(output, line); changed = TRUE; } if (changed && !failed) { if (o_stream_nfinish(output) < 0) { subswrite_set_syscall_error(list, "write()", path); failed = TRUE; } else if (mail_set->parsed_fsync_mode != FSYNC_MODE_NEVER) { if (fsync(fd_out) < 0) { subswrite_set_syscall_error(list, "fsync()", path); failed = TRUE; } } } else { o_stream_ignore_last_errors(output); } o_stream_destroy(&output); if (failed || !changed) { if (file_dotlock_delete(&dotlock) < 0) { subswrite_set_syscall_error(list, "file_dotlock_delete()", path); failed = TRUE; } } else { enum dotlock_replace_flags flags = DOTLOCK_REPLACE_FLAG_VERIFY_OWNER; if (file_dotlock_replace(&dotlock, flags) < 0) { subswrite_set_syscall_error(list, "file_dotlock_replace()", path); failed = TRUE; } } return failed ? -1 : (changed ? 1 : 0); }
const char *imapc_list_to_remote(struct imapc_mailbox_list *list, const char *name) { return mailbox_list_unescape_name_params(name, "", list->root_sep, mailbox_list_get_hierarchy_sep(&list->list), list->list.set.escape_char); }
static int maildir_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname) { const char *oldpath, *newpath, *root_path; int ret; bool found; /* NOTE: it's possible to rename a nonexistent mailbox which has children. In that case we should ignore the rename() error. */ if (mailbox_list_get_path(oldlist, oldname, MAILBOX_LIST_PATH_TYPE_MAILBOX, &oldpath) <= 0 || mailbox_list_get_path(newlist, newname, MAILBOX_LIST_PATH_TYPE_MAILBOX, &newpath) <= 0) i_unreached(); root_path = mailbox_list_get_root_forced(oldlist, MAILBOX_LIST_PATH_TYPE_MAILBOX); if (strcmp(oldpath, root_path) == 0) { /* most likely INBOX */ mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE, t_strdup_printf("Renaming %s isn't supported.", oldname)); return -1; } /* if we're renaming under another mailbox, require its permissions to be same as ours. */ if (strchr(newname, mailbox_list_get_hierarchy_sep(newlist)) != NULL) { struct mailbox_permissions old_perm, new_perm; mailbox_list_get_permissions(oldlist, oldname, &old_perm); mailbox_list_get_permissions(newlist, newname, &new_perm); if ((new_perm.file_create_mode != old_perm.file_create_mode || new_perm.dir_create_mode != old_perm.dir_create_mode || new_perm.file_create_gid != old_perm.file_create_gid)) { mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE, "Renaming not supported across conflicting " "directory permissions"); return -1; } } ret = rename(oldpath, newpath); if (ret == 0 || errno == ENOENT) { (void)rename_dir(oldlist, oldname, newlist, newname, MAILBOX_LIST_PATH_TYPE_CONTROL); (void)rename_dir(oldlist, oldname, newlist, newname, MAILBOX_LIST_PATH_TYPE_INDEX); found = ret == 0; T_BEGIN { ret = maildir_rename_children(oldlist, oldname, newlist, newname); } T_END; if (ret < 0) return -1; if (!found && ret == 0) { mailbox_list_set_error(oldlist, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(oldname)); return -1; } return 0; }