diff options
Diffstat (limited to 'src/lib-imap-storage/imap-metadata.c')
-rw-r--r-- | src/lib-imap-storage/imap-metadata.c | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/src/lib-imap-storage/imap-metadata.c b/src/lib-imap-storage/imap-metadata.c new file mode 100644 index 0000000..eb791a4 --- /dev/null +++ b/src/lib-imap-storage/imap-metadata.c @@ -0,0 +1,314 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "mail-storage.h" + +#include "imap-metadata.h" + +struct imap_metadata_transaction { + struct mailbox *box; + struct mailbox_transaction_context *trans; + + enum mail_error error; + char *error_string; + + bool server:1; + bool validated_only:1; +}; + +bool imap_metadata_verify_entry_name(const char *name, + const char **client_error_r) +{ + unsigned int i; + bool ok; + + if (name[0] != '/') { + *client_error_r = "Entry name must begin with '/'"; + return FALSE; + } + for (i = 0; name[i] != '\0'; i++) { + switch (name[i]) { + case '/': + if (i > 0 && name[i-1] == '/') { + *client_error_r = "Entry name can't contain consecutive '/'"; + return FALSE; + } + if (name[i+1] == '\0') { + *client_error_r = "Entry name can't end with '/'"; + return FALSE; + } + break; + case '*': + *client_error_r = "Entry name can't contain '*'"; + return FALSE; + case '%': + *client_error_r = "Entry name can't contain '%'"; + return FALSE; + default: + if (name[i] <= 0x19) { + *client_error_r = "Entry name can't contain control chars"; + return FALSE; + } + break; + } + } + T_BEGIN { + const char *prefix, *p = strchr(name+1, '/'); + + prefix = p == NULL ? name : t_strdup_until(name, p); + ok = strcasecmp(prefix, IMAP_METADATA_PRIVATE_PREFIX) == 0 || + strcasecmp(prefix, IMAP_METADATA_SHARED_PREFIX) == 0; + } T_END; + if (!ok) { + *client_error_r = "Entry name must begin with /private or /shared"; + return FALSE; + } + return TRUE; +} + +static void +imap_metadata_transaction_set_error(struct imap_metadata_transaction *imtrans, + enum mail_error error, const char *string) +{ + i_free(imtrans->error_string); + imtrans->error_string = i_strdup(string); + imtrans->error = error; +} + +static bool +imap_metadata_entry2key(struct imap_metadata_transaction *imtrans, + const char *entry, enum mail_attribute_type *type_r, + const char **key_r) +{ + const char *key_prefix = (imtrans->server ? + MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER : NULL); + + /* names are case-insensitive so we'll always lowercase them */ + entry = t_str_lcase(entry); + + if (str_begins(entry, IMAP_METADATA_PRIVATE_PREFIX)) { + *key_r = entry + strlen(IMAP_METADATA_PRIVATE_PREFIX); + *type_r = MAIL_ATTRIBUTE_TYPE_PRIVATE; + } else { + i_assert(str_begins(entry, IMAP_METADATA_SHARED_PREFIX)); + *key_r = entry + strlen(IMAP_METADATA_SHARED_PREFIX); + *type_r = MAIL_ATTRIBUTE_TYPE_SHARED; + } + if ((*key_r)[0] == '\0') { + /* /private or /shared prefix has no value itself */ + } else { + i_assert((*key_r)[0] == '/'); + *key_r += 1; + } + + if (imtrans->validated_only) + *type_r |= MAIL_ATTRIBUTE_TYPE_FLAG_VALIDATED; + + if (str_begins(*key_r, MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT)) { + /* Dovecot's internal attribute (mailbox or server). + don't allow accessing this. */ + return FALSE; + } + /* Add the server-prefix (after checking for the above internal + attribute). */ + if (key_prefix != NULL) + *key_r = t_strconcat(key_prefix, *key_r, NULL); + return TRUE; +} + +static int +imap_metadata_get_mailbox_transaction(struct imap_metadata_transaction *imtrans) +{ + if (imtrans->trans != NULL) + return 0; + + if (imtrans->box == NULL || mailbox_open(imtrans->box) < 0) + return -1; + imtrans->trans = mailbox_transaction_begin(imtrans->box, + MAILBOX_TRANSACTION_FLAG_EXTERNAL, __func__); + return 0; +} + +int imap_metadata_set(struct imap_metadata_transaction *imtrans, + const char *entry, const struct mail_attribute_value *value) +{ + enum mail_attribute_type type; + const char *key; + + if (!imap_metadata_entry2key(imtrans, entry, &type, &key)) { + imap_metadata_transaction_set_error(imtrans, MAIL_ERROR_PARAMS, + "Internal mailbox attributes cannot be accessed"); + return -1; + } + + if (imap_metadata_get_mailbox_transaction(imtrans) < 0) + return -1; + return (value->value == NULL && value->value_stream == NULL ? + mailbox_attribute_unset(imtrans->trans, type, key) : + mailbox_attribute_set(imtrans->trans, type, key, value)); +} + +int imap_metadata_unset(struct imap_metadata_transaction *imtrans, + const char *entry) +{ + struct mail_attribute_value value; + + i_zero(&value); + return imap_metadata_set(imtrans, entry, &value); +} + +int imap_metadata_get(struct imap_metadata_transaction *imtrans, + const char *entry, struct mail_attribute_value *value_r) +{ + enum mail_attribute_type type; + const char *key; + + i_zero(value_r); + if (!imap_metadata_entry2key(imtrans, entry, &type, &key)) + return 0; + return mailbox_attribute_get(imtrans->box, type, key, value_r); +} + +int imap_metadata_get_stream(struct imap_metadata_transaction *imtrans, + const char *entry, struct mail_attribute_value *value_r) +{ + enum mail_attribute_type type; + const char *key; + + i_zero(value_r); + if (!imap_metadata_entry2key(imtrans, entry, &type, &key)) + return 0; + return mailbox_attribute_get_stream(imtrans->box, type, key, value_r); +} + +struct imap_metadata_iter { + struct mailbox_attribute_iter *iter; +}; + +struct imap_metadata_iter * +imap_metadata_iter_init(struct imap_metadata_transaction *imtrans, + const char *entry) +{ + struct imap_metadata_iter *iter; + enum mail_attribute_type type; + const char *key; + + iter = i_new(struct imap_metadata_iter, 1); + if (imap_metadata_entry2key(imtrans, entry, &type, &key)) { + const char *prefix = + key[0] == '\0' ? "" : t_strconcat(key, "/", NULL); + iter->iter = mailbox_attribute_iter_init(imtrans->box, type, + prefix); + } + return iter; +} + +const char *imap_metadata_iter_next(struct imap_metadata_iter *iter) +{ + if (iter->iter == NULL) + return NULL; + return mailbox_attribute_iter_next(iter->iter); +} + +int imap_metadata_iter_deinit(struct imap_metadata_iter **_iter) +{ + struct imap_metadata_iter *iter = *_iter; + int ret; + + *_iter = NULL; + + if (iter->iter == NULL) + ret = 0; + else + ret = mailbox_attribute_iter_deinit(&iter->iter); + i_free(iter); + return ret; +} + +struct imap_metadata_transaction * +imap_metadata_transaction_begin(struct mailbox *box) +{ + struct imap_metadata_transaction *imtrans; + + imtrans = i_new(struct imap_metadata_transaction, 1); + imtrans->box = box; + return imtrans; +} + +struct imap_metadata_transaction * +imap_metadata_transaction_begin_server(struct mail_user *user) +{ + struct mail_namespace *ns; + struct mailbox *box; + struct imap_metadata_transaction *imtrans; + + ns = mail_namespace_find_inbox(user->namespaces); + /* Server metadata shouldn't depend on INBOX's ACLs, so ignore them. */ + box = mailbox_alloc(ns->list, "INBOX", MAILBOX_FLAG_IGNORE_ACLS | + MAILBOX_FLAG_ATTRIBUTE_SESSION); + imtrans = imap_metadata_transaction_begin(box); + imtrans->server = TRUE; + return imtrans; +} + +void imap_metadata_transaction_validated_only(struct imap_metadata_transaction *imtrans, + bool set) +{ + imtrans->validated_only = set; +} + +static void +imap_metadata_transaction_finish(struct imap_metadata_transaction **_imtrans) +{ + struct imap_metadata_transaction *imtrans = *_imtrans; + + if (imtrans->server) + mailbox_free(&imtrans->box); + + i_free(imtrans->error_string); + i_free(imtrans); + *_imtrans = NULL; +} + +int imap_metadata_transaction_commit( + struct imap_metadata_transaction **_imtrans, + enum mail_error *error_code_r, const char **client_error_r) +{ + struct imap_metadata_transaction *imtrans = *_imtrans; + int ret = 0; + + if (imtrans->trans != NULL) { + const char *error = NULL; + ret = mailbox_transaction_commit(&imtrans->trans); + if (ret < 0) + error = mailbox_get_last_error(imtrans->box, error_code_r); + if (client_error_r != NULL) + *client_error_r = error; + } + imap_metadata_transaction_finish(_imtrans); + return ret; +} + +void imap_metadata_transaction_rollback( + struct imap_metadata_transaction **_imtrans) +{ + struct imap_metadata_transaction *imtrans = *_imtrans; + + if (imtrans->trans != NULL) + mailbox_transaction_rollback(&imtrans->trans); + imap_metadata_transaction_finish(_imtrans); +} + +const char * +imap_metadata_transaction_get_last_error( + struct imap_metadata_transaction *imtrans, + enum mail_error *error_code_r) +{ + if (imtrans->error != MAIL_ERROR_NONE) { + if (error_code_r != NULL) + *error_code_r = imtrans->error; + return imtrans->error_string; + } + i_assert(imtrans->box != NULL); + return mailbox_get_last_error(imtrans->box, error_code_r); +} |