summaryrefslogtreecommitdiffstats
path: root/src/imap/imap-commands-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/imap/imap-commands-util.c')
-rw-r--r--src/imap/imap-commands-util.c402
1 files changed, 402 insertions, 0 deletions
diff --git a/src/imap/imap-commands-util.c b/src/imap/imap-commands-util.c
new file mode 100644
index 0000000..2d454a7
--- /dev/null
+++ b/src/imap/imap-commands-util.c
@@ -0,0 +1,402 @@
+/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
+
+#include "imap-common.h"
+#include "array.h"
+#include "buffer.h"
+#include "str.h"
+#include "str-sanitize.h"
+#include "imap-resp-code.h"
+#include "imap-parser.h"
+#include "imap-sync.h"
+#include "imap-utf7.h"
+#include "imap-util.h"
+#include "mail-storage.h"
+#include "mail-namespace.h"
+#include "imap-commands-util.h"
+
+struct mail_namespace *
+client_find_namespace_full(struct client *client,
+ const char **mailbox, const char **client_error_r)
+{
+ struct mail_namespace *namespaces = client->user->namespaces;
+ struct mail_namespace *ns;
+ string_t *utf8_name;
+
+ utf8_name = t_str_new(64);
+ if (imap_utf7_to_utf8(*mailbox, utf8_name) < 0) {
+ *client_error_r = "NO Mailbox name is not valid mUTF-7";
+ return NULL;
+ }
+
+ ns = mail_namespace_find(namespaces, str_c(utf8_name));
+ if ((ns->flags & NAMESPACE_FLAG_AUTOCREATED) != 0 &&
+ ns->prefix_len == 0) {
+ /* this matched only the autocreated prefix="" namespace.
+ give a nice human-readable error message */
+ *client_error_r = t_strdup_printf(
+ "NO Client tried to access nonexistent namespace. "
+ "(Mailbox name should probably be prefixed with: %s)",
+ mail_namespace_find_inbox(namespaces)->prefix);
+ return NULL;
+ }
+
+ if ((client->set->parsed_workarounds &
+ WORKAROUND_TB_EXTRA_MAILBOX_SEP) != 0 &&
+ str_len(utf8_name) > 0 &&
+ str_c(utf8_name)[str_len(utf8_name)-1] == mail_namespace_get_sep(ns)) {
+ /* drop the extra trailing hierarchy separator */
+ str_truncate(utf8_name, str_len(utf8_name)-1);
+ }
+
+ *mailbox = str_c(utf8_name);
+ return ns;
+}
+
+struct mail_namespace *
+client_find_namespace(struct client_command_context *cmd, const char **mailbox)
+{
+ struct mail_namespace *ns;
+ const char *client_error;
+
+ ns = client_find_namespace_full(cmd->client, mailbox, &client_error);
+ if (ns == NULL)
+ client_send_tagline(cmd, client_error);
+ return ns;
+}
+
+bool client_verify_open_mailbox(struct client_command_context *cmd)
+{
+ if (cmd->client->mailbox != NULL) {
+ event_add_str(cmd->global_event, "mailbox",
+ mailbox_get_vname(cmd->client->mailbox));
+ return TRUE;
+ } else {
+ client_send_tagline(cmd, "BAD No mailbox selected.");
+ return FALSE;
+ }
+}
+
+void imap_client_close_mailbox(struct client *client)
+{
+ struct mailbox *box;
+
+ i_assert(client->mailbox != NULL);
+
+ if (array_is_created(&client->fetch_failed_uids))
+ array_clear(&client->fetch_failed_uids);
+ client_search_updates_free(client);
+ array_free(&client->search_saved_uidset);
+
+ box = client->mailbox;
+ client->mailbox = NULL;
+
+ mailbox_free(&box);
+ client_update_mailbox_flags(client, NULL);
+}
+
+int client_open_save_dest_box(struct client_command_context *cmd,
+ const char *name, struct mailbox **destbox_r)
+{
+ struct mail_namespace *ns;
+ struct mailbox *box;
+ const char *error_string;
+ enum mail_error error;
+
+ ns = client_find_namespace(cmd, &name);
+ if (ns == NULL)
+ return -1;
+
+ if (cmd->client->mailbox != NULL &&
+ mailbox_equals(cmd->client->mailbox, ns, name)) {
+ *destbox_r = cmd->client->mailbox;
+ return 0;
+ }
+ box = mailbox_alloc(ns->list, name, MAILBOX_FLAG_SAVEONLY);
+ if (mailbox_open(box) < 0) {
+ error_string = mailbox_get_last_error(box, &error);
+ if (error == MAIL_ERROR_NOTFOUND) {
+ client_send_tagline(cmd, t_strdup_printf(
+ "NO [TRYCREATE] %s", error_string));
+ } else {
+ client_send_box_error(cmd, box);
+ }
+ mailbox_free(&box);
+ return -1;
+ }
+ if (mailbox_enable(box, client_enabled_mailbox_features(cmd->client)) < 0) {
+ client_send_box_error(cmd, box);
+ mailbox_free(&box);
+ return -1;
+ }
+ *destbox_r = box;
+ return 0;
+}
+
+const char *imap_client_command_get_reason(struct client_command_context *cmd)
+{
+ return cmd->args[0] == '\0' ? cmd->name :
+ t_strdup_printf("%s %s", cmd->name, cmd->human_args);
+}
+
+const char *
+imap_get_error_string(struct client_command_context *cmd,
+ const char *error_string, enum mail_error error)
+{
+ const char *resp_code = NULL;
+
+ switch (error) {
+ case MAIL_ERROR_NONE:
+ break;
+ case MAIL_ERROR_TEMP:
+ case MAIL_ERROR_LOOKUP_ABORTED: /* BUG: shouldn't be visible here */
+ resp_code = IMAP_RESP_CODE_SERVERBUG;
+ break;
+ case MAIL_ERROR_UNAVAILABLE:
+ resp_code = IMAP_RESP_CODE_UNAVAILABLE;
+ break;
+ case MAIL_ERROR_NOTPOSSIBLE:
+ case MAIL_ERROR_PARAMS:
+ resp_code = IMAP_RESP_CODE_CANNOT;
+ break;
+ case MAIL_ERROR_PERM:
+ resp_code = IMAP_RESP_CODE_NOPERM;
+ break;
+ case MAIL_ERROR_NOQUOTA:
+ resp_code = IMAP_RESP_CODE_OVERQUOTA;
+ break;
+ case MAIL_ERROR_NOTFOUND:
+ if ((cmd->cmd_flags & COMMAND_FLAG_USE_NONEXISTENT) != 0)
+ resp_code = IMAP_RESP_CODE_NONEXISTENT;
+ break;
+ case MAIL_ERROR_EXISTS:
+ resp_code = IMAP_RESP_CODE_ALREADYEXISTS;
+ break;
+ case MAIL_ERROR_EXPUNGED:
+ resp_code = IMAP_RESP_CODE_EXPUNGEISSUED;
+ break;
+ case MAIL_ERROR_INUSE:
+ resp_code = IMAP_RESP_CODE_INUSE;
+ break;
+ case MAIL_ERROR_CONVERSION:
+ case MAIL_ERROR_INVALIDDATA:
+ break;
+ case MAIL_ERROR_LIMIT:
+ resp_code = IMAP_RESP_CODE_LIMIT;
+ break;
+ }
+ if (resp_code == NULL || *error_string == '[')
+ return t_strconcat("NO ", error_string, NULL);
+ else
+ return t_strdup_printf("NO [%s] %s", resp_code, error_string);
+}
+
+void client_send_error(struct client_command_context *cmd,
+ const char *error_string, enum mail_error error)
+{
+ client_send_tagline(cmd, imap_get_error_string(cmd, error_string,
+ error));
+ client_disconnect_if_inconsistent(cmd->client);
+}
+
+void client_send_list_error(struct client_command_context *cmd,
+ struct mailbox_list *list)
+{
+ const char *error_string;
+ enum mail_error error;
+
+ error_string = mailbox_list_get_last_error(list, &error);
+ client_send_tagline(cmd, imap_get_error_string(cmd, error_string,
+ error));
+}
+
+void client_disconnect_if_inconsistent(struct client *client)
+{
+ if (client->mailbox != NULL &&
+ mailbox_is_inconsistent(client->mailbox)) {
+ /* we can't do forced CLOSE, so have to disconnect */
+ client_disconnect_with_error(client,
+ "IMAP session state is inconsistent, please relogin.");
+ }
+}
+
+void client_send_box_error(struct client_command_context *cmd,
+ struct mailbox *box)
+{
+ client_send_storage_error(cmd, mailbox_get_storage(box));
+}
+
+void client_send_storage_error(struct client_command_context *cmd,
+ struct mail_storage *storage)
+{
+ const char *error_string;
+ enum mail_error error;
+
+ error_string = mail_storage_get_last_error(storage, &error);
+ client_send_error(cmd, error_string, error);
+}
+
+void client_send_untagged_storage_error(struct client *client,
+ struct mail_storage *storage)
+{
+ const char *error_string;
+ enum mail_error error;
+
+ error_string = mail_storage_get_last_error(storage, &error);
+ client_send_line(client, t_strconcat("* NO ", error_string, NULL));
+
+ client_disconnect_if_inconsistent(client);
+}
+
+bool client_parse_mail_flags(struct client_command_context *cmd,
+ const struct imap_arg *args,
+ enum mail_flags *flags_r,
+ const char *const **keywords_r)
+{
+ const char *atom;
+ enum mail_flags flag;
+ ARRAY(const char *) keywords;
+
+ *flags_r = 0;
+ *keywords_r = NULL;
+ p_array_init(&keywords, cmd->pool, 16);
+
+ while (!IMAP_ARG_IS_EOL(args)) {
+ if (!imap_arg_get_atom(args, &atom)) {
+ client_send_command_error(cmd,
+ "Flags list contains non-atoms.");
+ return FALSE;
+ }
+
+ if (*atom == '\\') {
+ /* system flag */
+ atom = t_str_ucase(atom);
+ flag = imap_parse_system_flag(atom);
+ if (flag != 0 && flag != MAIL_RECENT)
+ *flags_r |= flag;
+ else {
+ client_send_command_error(cmd, t_strconcat(
+ "Invalid system flag ", atom, NULL));
+ return FALSE;
+ }
+ } else {
+ /* keyword validity checks are done by lib-storage */
+ array_push_back(&keywords, &atom);
+ }
+
+ args++;
+ }
+
+ if (array_count(&keywords) == 0)
+ *keywords_r = NULL;
+ else {
+ array_append_zero(&keywords); /* NULL-terminate */
+ *keywords_r = array_front(&keywords);
+ }
+ return TRUE;
+}
+
+void client_send_mailbox_flags(struct client *client, bool selecting)
+{
+ struct mailbox_status status;
+ unsigned int count = array_count(client->keywords.names);
+ const char *const *keywords;
+ string_t *str;
+
+ if (!selecting && count == client->keywords.announce_count) {
+ /* no changes to keywords and we're not selecting a mailbox */
+ return;
+ }
+
+ client->keywords.announce_count = count;
+ mailbox_get_open_status(client->mailbox, STATUS_PERMANENT_FLAGS,
+ &status);
+
+ keywords = count == 0 ? NULL :
+ array_front(client->keywords.names);
+ str = t_str_new(128);
+ str_append(str, "* FLAGS (");
+ imap_write_flags(str, status.flags, keywords);
+ str_append_c(str, ')');
+ client_send_line(client, str_c(str));
+
+ if (!status.permanent_keywords)
+ keywords = NULL;
+
+ str_truncate(str, 0);
+ str_append(str, "* OK [PERMANENTFLAGS (");
+ imap_write_flags(str, status.permanent_flags, keywords);
+ if (status.allow_new_keywords) {
+ if (status.permanent_flags != 0 || keywords != NULL)
+ str_append_c(str, ' ');
+ str_append(str, "\\*");
+ }
+ str_append(str, ")] ");
+
+ if (mailbox_is_readonly(client->mailbox))
+ str_append(str, "Read-only mailbox.");
+ else
+ str_append(str, "Flags permitted.");
+ client_send_line(client, str_c(str));
+}
+
+void client_update_mailbox_flags(struct client *client,
+ const ARRAY_TYPE(keywords) *keywords)
+{
+ client->keywords.names = keywords;
+ client->keywords.announce_count = 0;
+}
+
+const char *const *
+client_get_keyword_names(struct client *client, ARRAY_TYPE(keywords) *dest,
+ const ARRAY_TYPE(keyword_indexes) *src)
+{
+ unsigned int kw_index;
+ const char *const *all_names;
+ unsigned int all_count;
+
+ client_send_mailbox_flags(client, FALSE);
+
+ /* convert indexes to names */
+ all_names = array_get(client->keywords.names, &all_count);
+ array_clear(dest);
+ array_foreach_elem(src, kw_index) {
+ i_assert(kw_index < all_count);
+ array_push_back(dest, &all_names[kw_index]);
+ }
+
+ array_append_zero(dest);
+ return array_front(dest);
+}
+
+void msgset_generator_init(struct msgset_generator_context *ctx, string_t *str)
+{
+ i_zero(ctx);
+ ctx->str = str;
+ ctx->last_uid = (uint32_t)-1;
+}
+
+void msgset_generator_next(struct msgset_generator_context *ctx, uint32_t uid)
+{
+ i_assert(uid > 0);
+
+ if (uid-1 != ctx->last_uid) {
+ if (ctx->first_uid == 0)
+ ;
+ else if (ctx->first_uid == ctx->last_uid)
+ str_printfa(ctx->str, "%u,", ctx->first_uid);
+ else {
+ str_printfa(ctx->str, "%u:%u,",
+ ctx->first_uid, ctx->last_uid);
+ }
+ ctx->first_uid = uid;
+ }
+ ctx->last_uid = uid;
+}
+
+void msgset_generator_finish(struct msgset_generator_context *ctx)
+{
+ if (ctx->first_uid == ctx->last_uid)
+ str_printfa(ctx->str, "%u", ctx->first_uid);
+ else
+ str_printfa(ctx->str, "%u:%u", ctx->first_uid, ctx->last_uid);
+}