LIBDOVECOT_INCLUDE="-I$(incdir) -I$(incdir)/src/lib -I$(incdir)/src/lib-dict -I$(incdir)/src/lib-dns -I$(incdir)/src/lib-http -I$(incdir)/src/lib-mail -I$(incdir)/src/lib-imap -I$(incdir)/src/lib-fs -I$(incdir)/src/lib-charset -I$(incdir)/src/lib-auth -I$(incdir)/src/lib-master -I$(incdir)/src/lib-ssl-iostream -I$(incdir)/src/lib-compression -I$(incdir)/src/lib-settings -I$(incdir)/src/lib-test"
LIBDOVECOT_LDA_INCLUDE="-I$(incdir)/src/lib-lda -I$(incdir)/src/lda"
LIBDOVECOT_DOVEADM_INCLUDE="-I$(incdir)/src/doveadm"
-LIBDOVECOT_STORAGE_INCLUDE="-I$(incdir)/src/lib-index -I$(incdir)/src/lib-storage -I$(incdir)/src/lib-storage/list -I$(incdir)/src/lib-storage/index -I$(incdir)/src/lib-storage/index/raw -I$(incdir)/src/plugins/quota"
+LIBDOVECOT_STORAGE_INCLUDE="-I$(incdir)/src/lib-index -I$(incdir)/src/lib-storage -I$(incdir)/src/lib-storage/list -I$(incdir)/src/lib-storage/index -I$(incdir)/src/lib-storage/index/raw -I$(incdir)/src/lib-imap-storage -I$(incdir)/src/plugins/quota"
LIBDOVECOT_DSYNC_INCLUDE="-I$(incdir)/src/doveadm/dsync"
LIBDOVECOT_LOGIN_INCLUDE="-I$(incdir)/src/login-common"
LIBDOVECOT_IMAP_INCLUDE="-I$(incdir)/src/imap"
imap-fetch.c \
imap-fetch-body.c \
imap-list.c \
- imap-metadata.c \
imap-notify.c \
imap-search.c \
imap-search-args.c \
imap-expunge.h \
imap-fetch.h \
imap-list.h \
- imap-metadata.h \
imap-notify.h \
imap-search.h \
imap-search-args.h \
struct client_command_context *cmd;
struct mailbox *box;
- struct mailbox_transaction_context *trans;
+ struct imap_metadata_transaction *trans;
struct mailbox_list_iterate_context *list_iter;
ARRAY_TYPE(const_string) entries;
struct istream *cur_stream;
uoff_t cur_stream_offset, cur_stream_size;
- struct mailbox_attribute_iter *iter;
+ struct imap_metadata_iter *iter;
string_t *iter_entry_prefix;
- const char *key_prefix;
unsigned int entry_idx;
bool first_entry_sent;
bool failed;
};
-static bool cmd_getmetadata_iter_next(struct imap_getmetadata_context *ctx);
+static bool
+cmd_getmetadata_mailbox_iter_next(struct imap_getmetadata_context *ctx);
static bool
cmd_getmetadata_parse_options(struct imap_getmetadata_context *ctx,
imap_metadata_parse_entry_names(struct imap_getmetadata_context *ctx,
const struct imap_arg *entries)
{
- const char *value;
+ const char *value, *error;
p_array_init(&ctx->entries, ctx->cmd->pool, 4);
for (; !IMAP_ARG_IS_EOL(entries); entries++) {
client_send_command_error(ctx->cmd, "Entry isn't astring");
return FALSE;
}
- if (!imap_metadata_verify_entry_name(ctx->cmd, value))
+ if (!imap_metadata_verify_entry_name(value, &error)) {
+ client_send_command_error(ctx->cmd, error);
return FALSE;
+ }
/* names are case-insensitive so we'll always lowercase them */
value = p_strdup(ctx->cmd->pool, t_str_lcase(value));
ctx->first_entry_sent = TRUE;
str_append(str, "* METADATA ");
- if (imap_utf8_to_utf7(mailbox_get_vname(ctx->box), mailbox_mutf7) < 0)
- i_unreached();
- imap_append_astring(str, str_c(mailbox_mutf7));
+ if (ctx->box == NULL) {
+ /* server metadata reply */
+ str_append(str, "\"\"");
+ } else {
+ if (imap_utf8_to_utf7(mailbox_get_vname(ctx->box), mailbox_mutf7) < 0)
+ i_unreached();
+ imap_append_astring(str, str_c(mailbox_mutf7));
+ }
str_append(str, " (");
/* nothing can be sent until untagged METADATA is finished */
static void cmd_getmetadata_send_entry(struct imap_getmetadata_context *ctx,
const char *entry, bool require_reply)
{
- enum mail_attribute_type type;
+ struct client *client = ctx->cmd->client;
struct mail_attribute_value value;
+ const char *error_string;
enum mail_error error;
uoff_t value_len;
- const char *key;
string_t *str;
- imap_metadata_entry2key(entry, ctx->key_prefix, &type, &key);
- if (ctx->key_prefix == NULL &&
- strncmp(key, MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT,
- strlen(MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT)) == 0) {
- /* skip over dovecot's internal attributes. (if key_prefix
- isn't NULL, we're getting server metadata, which is handled
- inside the private metadata.) */
- if (require_reply)
- cmd_getmetadata_send_nil_reply(ctx, entry);
- return;
- }
-
- if (mailbox_attribute_get_stream(ctx->trans, type, key, &value) < 0) {
- (void)mailbox_get_last_error(ctx->box, &error);
+ if (imap_metadata_get_stream(ctx->trans, entry, &value) < 0) {
+ error_string = imap_metadata_transaction_get_last_error(
+ ctx->trans, &error);
if (error != MAIL_ERROR_NOTFOUND && error != MAIL_ERROR_PERM) {
- client_send_untagged_storage_error(ctx->cmd->client,
- mailbox_get_storage(ctx->box));
+ client_send_line(client, t_strconcat("* NO ", error_string, NULL));
ctx->failed = TRUE;
}
}
+
if (value.value != NULL)
value_len = strlen(value.value);
else if (value.value_stream != NULL) {
str = metadata_add_entry(ctx, entry);
if (value.value != NULL) {
str_printfa(str, " {%"PRIuUOFF_T"}\r\n%s", value_len, value.value);
- o_stream_send(ctx->cmd->client->output, str_data(str), str_len(str));
+ o_stream_nsend(client->output, str_data(str), str_len(str));
} else {
str_printfa(str, " ~{%"PRIuUOFF_T"}\r\n", value_len);
- o_stream_send(ctx->cmd->client->output, str_data(str), str_len(str));
+ o_stream_nsend(client->output, str_data(str), str_len(str));
ctx->cur_stream_offset = 0;
ctx->cur_stream_size = value_len;
return FALSE;
}
-static int cmd_getmetadata_send_entry_tree(struct imap_getmetadata_context *ctx,
+static int
+cmd_getmetadata_send_entry_tree(struct imap_getmetadata_context *ctx,
const char *entry)
{
- const char *key;
- enum mail_attribute_type type;
+ struct client *client = ctx->cmd->client;
- if (o_stream_get_buffer_used_size(ctx->cmd->client->output) >=
+ if (o_stream_get_buffer_used_size(client->output) >=
CLIENT_OUTPUT_OPTIMAL_SIZE) {
- if (o_stream_flush(ctx->cmd->client->output) <= 0) {
- o_stream_set_flush_pending(ctx->cmd->client->output, TRUE);
+ if (o_stream_flush(client->output) <= 0) {
+ o_stream_set_flush_pending(client->output, TRUE);
return 0;
}
}
if (ctx->iter != NULL) {
+ const char *subentry;
+
/* DEPTH iteration */
do {
- key = mailbox_attribute_iter_next(ctx->iter);
- if (key == NULL) {
+ subentry = imap_metadata_iter_next(ctx->iter);
+ if (subentry == NULL) {
/* iteration finished, get to the next entry */
- if (mailbox_attribute_iter_deinit(&ctx->iter) < 0) {
- client_send_untagged_storage_error(ctx->cmd->client,
- mailbox_get_storage(ctx->box));
+ if (imap_metadata_iter_deinit(&ctx->iter) < 0) {
+ enum mail_error error;
+ client_send_line(client, t_strconcat("* NO ",
+ imap_metadata_transaction_get_last_error(ctx->trans, &error),
+ NULL));
ctx->failed = TRUE;
}
return -1;
}
- } while (ctx->depth == 1 && strchr(key, '/') != NULL);
- entry = t_strconcat(str_c(ctx->iter_entry_prefix), key, NULL);
+ } while (ctx->depth == 1 && strchr(subentry, '/') != NULL);
+ entry = t_strconcat(str_c(ctx->iter_entry_prefix), subentry, NULL);
}
cmd_getmetadata_send_entry(ctx, entry, ctx->iter == NULL);
str_append(ctx->iter_entry_prefix, entry);
str_append_c(ctx->iter_entry_prefix, '/');
- imap_metadata_entry2key(entry, ctx->key_prefix, &type, &key);
- ctx->iter = mailbox_attribute_iter_init(ctx->box, type,
- key[0] == '\0' ? "" : t_strconcat(key, "/", NULL));
+ ctx->iter = imap_metadata_iter_init(ctx->trans, entry);
return 1;
}
}
-static void cmd_getmetadata_mailbox_deinit(struct imap_getmetadata_context *ctx)
+static void cmd_getmetadata_iter_deinit(struct imap_getmetadata_context *ctx)
{
if (ctx->iter != NULL)
- (void)mailbox_attribute_iter_deinit(&ctx->iter);
- if (ctx->box != NULL) {
- (void)mailbox_transaction_commit(&ctx->trans);
+ (void)imap_metadata_iter_deinit(&ctx->iter);
+ if (ctx->trans != NULL)
+ (void)imap_metadata_transaction_commit(&ctx->trans, NULL, NULL);
+ if (ctx->box != NULL)
mailbox_free(&ctx->box);
- }
ctx->first_entry_sent = FALSE;
ctx->entry_idx = 0;
}
{
struct client_command_context *cmd = ctx->cmd;
- cmd_getmetadata_mailbox_deinit(ctx);
+ cmd_getmetadata_iter_deinit(ctx);
cmd->client->output_cmd_lock = NULL;
if (ctx->list_iter != NULL &&
if (ctx->first_entry_sent)
o_stream_nsend_str(cmd->client->output, ")\r\n");
- cmd_getmetadata_mailbox_deinit(ctx);
+ cmd_getmetadata_iter_deinit(ctx);
if (ctx->list_iter != NULL)
- return cmd_getmetadata_iter_next(ctx);
+ return cmd_getmetadata_mailbox_iter_next(ctx);
cmd_getmetadata_deinit(ctx);
return TRUE;
}
-static int
-cmd_getmetadata_try_mailbox(struct imap_getmetadata_context *ctx,
- struct mail_namespace *ns, const char *mailbox)
+static bool
+cmd_getmetadata_start(struct imap_getmetadata_context *ctx)
{
struct client_command_context *cmd = ctx->cmd;
- ctx->box = mailbox_alloc(ns->list, mailbox, MAILBOX_FLAG_READONLY);
- if (mailbox_open(ctx->box) < 0)
- return -1;
- ctx->trans = mailbox_transaction_begin(ctx->box, 0);
-
if (ctx->depth > 0)
ctx->iter_entry_prefix = str_new(cmd->pool, 128);
if (!cmd_getmetadata_continue(cmd)) {
cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT;
cmd->func = cmd_getmetadata_continue;
- return 0;
+ return FALSE;
}
- return 1;
+ return TRUE;
+}
+
+static bool
+cmd_getmetadata_server(struct imap_getmetadata_context *ctx)
+{
+ ctx->trans = imap_metadata_transaction_begin_server(ctx->cmd->client->user);
+ return cmd_getmetadata_start(ctx);
+}
+
+static int
+cmd_getmetadata_try_mailbox(struct imap_getmetadata_context *ctx,
+ struct mail_namespace *ns, const char *mailbox)
+{
+ ctx->box = mailbox_alloc(ns->list, mailbox, MAILBOX_FLAG_READONLY);
+ if (mailbox_open(ctx->box) < 0)
+ return -1;
+
+ ctx->trans = imap_metadata_transaction_begin(ctx->box);
+ return cmd_getmetadata_start(ctx) ? 1 : 0;
}
static bool
return ret != 0;
}
-static bool cmd_getmetadata_iter_next(struct imap_getmetadata_context *ctx)
+static bool
+cmd_getmetadata_mailbox_iter_next(struct imap_getmetadata_context *ctx)
{
const struct mailbox_info *info;
int ret;
if (mailbox[0] == '\0') {
/* server attribute */
- ctx->key_prefix = MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER;
- ns = mail_namespace_find_inbox(cmd->client->user->namespaces);
- return cmd_getmetadata_mailbox(ctx, ns, "INBOX");
+ return cmd_getmetadata_server(ctx);
} else if (strchr(mailbox, '*') == NULL &&
strchr(mailbox, '%') == NULL) {
+ /* mailbox attribute */
ns = client_find_namespace(cmd, &mailbox);
if (ns == NULL)
return TRUE;
mailbox_list_iter_init_namespaces(
cmd->client->user->namespaces,
patterns, MAIL_NAMESPACE_TYPE_MASK_ALL, 0);
- return cmd_getmetadata_iter_next(ctx);
+ return cmd_getmetadata_mailbox_iter_next(ctx);
}
}
struct client_command_context *cmd;
struct imap_parser *parser;
- const char *key_prefix;
-
struct mailbox *box;
- struct mailbox_transaction_context *trans;
+ struct imap_metadata_transaction *trans;
char *entry_name;
uoff_t entry_value_len;
ctx->cmd->client->input_lock = NULL;
imap_parser_unref(&ctx->parser);
if (ctx->trans != NULL)
- mailbox_transaction_rollback(&ctx->trans);
- if (ctx->box != ctx->cmd->client->mailbox)
+ imap_metadata_transaction_rollback(&ctx->trans);
+ if (ctx->box != NULL && ctx->box != ctx->cmd->client->mailbox)
mailbox_free(&ctx->box);
i_free(ctx->entry_name);
}
client_send_command_error(ctx->cmd, "Entry value can't be a list");
return -1;
}
- if (ctx->cmd_error_sent ||
- !imap_metadata_verify_entry_name(ctx->cmd, name)) {
+ if (!ctx->cmd_error_sent &&
+ !imap_metadata_verify_entry_name(name, &error)) {
+ client_send_command_error(ctx->cmd, error);
+ ctx->cmd_error_sent = TRUE;
+ }
+ if (ctx->cmd_error_sent) {
ctx->cmd->param_error = FALSE;
ctx->cmd->state = CLIENT_COMMAND_STATE_WAIT_INPUT;
- ctx->cmd_error_sent = TRUE;
ctx->failed = TRUE;
if (args[1].type == IMAP_ARG_LITERAL_SIZE) {
/* client won't see "+ OK", so we can abort
{
const unsigned char *data;
size_t size;
- enum mail_attribute_type type;
- const char *key;
struct mail_attribute_value value;
int ret;
return 1;
}
- imap_metadata_entry2key(ctx->entry_name, ctx->key_prefix,
- &type, &key);
memset(&value, 0, sizeof(value));
value.value_stream = ctx->input;
- if (mailbox_attribute_set(ctx->trans, type, key, &value) < 0) {
+ if (imap_metadata_set(ctx->trans, ctx->entry_name, &value) < 0) {
/* delay reporting the failure so we'll finish
reading the command input */
ctx->storage_failure = TRUE;
const struct imap_arg *entry_value)
{
struct istream *inputs[2];
- enum mail_attribute_type type;
- const char *key;
struct mail_attribute_value value;
string_t *path;
int ret;
case IMAP_ARG_ATOM:
case IMAP_ARG_STRING:
/* we have the value already */
- imap_metadata_entry2key(entry_name, ctx->key_prefix,
- &type, &key);
if (ctx->failed)
return 1;
memset(&value, 0, sizeof(value));
value.value = imap_arg_as_nstring(entry_value);
- ret = value.value == NULL ?
- mailbox_attribute_unset(ctx->trans, type, key) :
- mailbox_attribute_set(ctx->trans, type, key, &value);
+ ret = imap_metadata_set(ctx->trans, entry_name, &value);
if (ret < 0) {
/* delay reporting the failure so we'll finish
reading the command input */
static bool cmd_setmetadata_continue(struct client_command_context *cmd)
{
struct imap_setmetadata_context *ctx = cmd->context;
- const char *entry;
+ const char *entry, *error_string;
+ enum mail_error error;
const struct imap_arg *value;
int ret;
if (ret == 0)
return 0;
- if (ret < 0 || ctx->cmd_error_sent)
+ if (ret < 0 || ctx->cmd_error_sent) {
/* already sent the error to client */ ;
- else if (ctx->storage_failure)
- client_send_box_error(cmd, ctx->box);
- else if (mailbox_transaction_commit(&ctx->trans) < 0)
- client_send_box_error(cmd, ctx->box);
- else
+ } else if (ctx->storage_failure) {
+ if (ctx->box == NULL)
+ client_disconnect_if_inconsistent(cmd->client);
+ error_string = imap_metadata_transaction_get_last_error
+ (ctx->trans, &error);
+ client_send_tagline(cmd,
+ imap_get_error_string(cmd, error_string, error));
+ } else if (imap_metadata_transaction_commit(&ctx->trans,
+ &error, &error_string) < 0) {
+ if (ctx->box == NULL)
+ client_disconnect_if_inconsistent(cmd->client);
+ client_send_tagline(cmd,
+ imap_get_error_string(cmd, error_string, error));
+ } else {
client_send_tagline(cmd, "OK Setmetadata completed.");
+ }
cmd_setmetadata_deinit(ctx);
return TRUE;
}
+static bool
+cmd_setmetadata_start(struct imap_setmetadata_context *ctx)
+{
+ struct client_command_context *cmd = ctx->cmd;
+ struct client *client = cmd->client;
+
+ /* we support large literals, so read the values from client
+ asynchronously the same way as APPEND does. */
+ client->input_lock = cmd;
+ ctx->parser = imap_parser_create(client->input, client->output,
+ client->set->imap_max_line_length);
+ o_stream_unset_flush_callback(client->output);
+
+ cmd->func = cmd_setmetadata_continue;
+ cmd->context = ctx;
+ return cmd_setmetadata_continue(cmd);
+}
+
+static bool
+cmd_setmetadata_server(struct imap_setmetadata_context *ctx)
+{
+ ctx->trans = imap_metadata_transaction_begin_server(ctx->cmd->client->user);
+ return cmd_setmetadata_start(ctx);
+}
+
+static bool
+cmd_setmetadata_mailbox(struct imap_setmetadata_context *ctx,
+ const char *mailbox)
+{
+ struct client_command_context *cmd = ctx->cmd;
+ struct client *client = cmd->client;
+ struct mail_namespace *ns;
+
+ ns = client_find_namespace(cmd, &mailbox);
+ if (ns == NULL)
+ return TRUE;
+
+ if (client->mailbox != NULL && !client->mailbox_examined &&
+ mailbox_equals(client->mailbox, ns, mailbox))
+ ctx->box = client->mailbox;
+ else {
+ ctx->box = mailbox_alloc(ns->list, mailbox, 0);
+ if (mailbox_open(ctx->box) < 0) {
+ client_send_box_error(cmd, ctx->box);
+ mailbox_free(&ctx->box);
+ return TRUE;
+ }
+ }
+ ctx->trans = imap_metadata_transaction_begin(ctx->box);
+ return cmd_setmetadata_start(ctx);
+}
+
bool cmd_setmetadata(struct client_command_context *cmd)
{
struct imap_setmetadata_context *ctx;
const struct imap_arg *args;
const char *mailbox;
- struct mail_namespace *ns;
int ret;
ret = imap_parser_read_args(cmd->parser, 2,
if (mailbox[0] == '\0') {
/* server attribute */
- ctx->key_prefix = MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER;
- ns = mail_namespace_find_inbox(cmd->client->user->namespaces);
- mailbox = "INBOX";
- } else {
- ns = client_find_namespace(cmd, &mailbox);
- if (ns == NULL)
- return TRUE;
- }
-
- if (cmd->client->mailbox != NULL && !cmd->client->mailbox_examined &&
- mailbox_equals(cmd->client->mailbox, ns, mailbox))
- ctx->box = cmd->client->mailbox;
- else {
- ctx->box = mailbox_alloc(ns->list, mailbox, 0);
- if (mailbox_open(ctx->box) < 0) {
- client_send_box_error(cmd, ctx->box);
- mailbox_free(&ctx->box);
- return TRUE;
- }
+ return cmd_setmetadata_server(ctx);
}
- ctx->trans = mailbox_transaction_begin(ctx->box, 0);
- /* we support large literals, so read the values from client
- asynchronously the same way as APPEND does. */
- cmd->client->input_lock = cmd;
- ctx->parser = imap_parser_create(cmd->client->input, cmd->client->output,
- cmd->client->set->imap_max_line_length);
- o_stream_unset_flush_callback(cmd->client->output);
- cmd->func = cmd_setmetadata_continue;
- cmd->context = ctx;
- return cmd_setmetadata_continue(cmd);
+ return cmd_setmetadata_mailbox(ctx, mailbox);
}
+++ /dev/null
-/* Copyright (c) 2013-2014 Dovecot authors, see the included COPYING file */
-
-#include "imap-common.h"
-#include "imap-metadata.h"
-
-bool imap_metadata_verify_entry_name(struct client_command_context *cmd,
- const char *name)
-{
- unsigned int i;
- bool ok;
-
- if (name[0] != '/') {
- client_send_command_error(cmd,
- "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_send_command_error(cmd,
- "Entry name can't contain consecutive '/'");
- return FALSE;
- }
- if (name[i+1] == '\0') {
- client_send_command_error(cmd,
- "Entry name can't end with '/'");
- return FALSE;
- }
- break;
- case '*':
- client_send_command_error(cmd,
- "Entry name can't contain '*'");
- return FALSE;
- case '%':
- client_send_command_error(cmd,
- "Entry name can't contain '%'");
- return FALSE;
- default:
- if (name[i] <= 0x19) {
- client_send_command_error(cmd,
- "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_send_command_error(cmd,
- "Entry name must begin with /private or /shared");
- return FALSE;
- }
- return TRUE;
-}
-
-void imap_metadata_entry2key(const char *entry, const char *key_prefix,
- enum mail_attribute_type *type_r,
- const char **key_r)
-{
- if (strncmp(entry, IMAP_METADATA_PRIVATE_PREFIX,
- strlen(IMAP_METADATA_PRIVATE_PREFIX)) == 0) {
- *key_r = entry + strlen(IMAP_METADATA_PRIVATE_PREFIX);
- *type_r = MAIL_ATTRIBUTE_TYPE_PRIVATE;
- } else {
- i_assert(strncmp(entry, IMAP_METADATA_SHARED_PREFIX,
- strlen(IMAP_METADATA_SHARED_PREFIX)) == 0);
- *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 (key_prefix != NULL)
- *key_r = t_strconcat(key_prefix, *key_r, NULL);
-}
+++ /dev/null
-#ifndef IMAP_METADATA_H
-#define IMAP_METADATA_H
-
-#define IMAP_METADATA_PRIVATE_PREFIX "/private"
-#define IMAP_METADATA_SHARED_PREFIX "/shared"
-
-bool imap_metadata_verify_entry_name(struct client_command_context *cmd,
- const char *name);
-void imap_metadata_entry2key(const char *entry, const char *key_prefix,
- enum mail_attribute_type *type_r,
- const char **key_r);
-
-#endif
libimap_storage_la_SOURCES = \
imap-msgpart.c \
- imap-msgpart-url.c
+ imap-msgpart-url.c \
+ imap-metadata.c
headers = \
imap-msgpart.h \
- imap-msgpart-url.h
+ imap-msgpart-url.h \
+ imap-metadata.h
pkginc_libdir=$(pkgincludedir)
pkginc_lib_HEADERS = $(headers)
--- /dev/null
+/* Copyright (c) 2013-2014 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;
+
+ unsigned int server:1;
+};
+
+bool imap_metadata_verify_entry_name(const char *name, const char **error_r)
+{
+ unsigned int i;
+ bool ok;
+
+ if (name[0] != '/') {
+ *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] == '/') {
+ *error_r = "Entry name can't contain consecutive '/'";
+ return FALSE;
+ }
+ if (name[i+1] == '\0') {
+ *error_r = "Entry name can't end with '/'";
+ return FALSE;
+ }
+ break;
+ case '*':
+ *error_r = "Entry name can't contain '*'";
+ return FALSE;
+ case '%':
+ *error_r = "Entry name can't contain '%'";
+ return FALSE;
+ default:
+ if (name[i] <= 0x19) {
+ *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) {
+ *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 (strncmp(entry, IMAP_METADATA_PRIVATE_PREFIX,
+ strlen(IMAP_METADATA_PRIVATE_PREFIX)) == 0) {
+ *key_r = entry + strlen(IMAP_METADATA_PRIVATE_PREFIX);
+ *type_r = MAIL_ATTRIBUTE_TYPE_PRIVATE;
+ } else {
+ i_assert(strncmp(entry, IMAP_METADATA_SHARED_PREFIX,
+ strlen(IMAP_METADATA_SHARED_PREFIX)) == 0);
+ *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 (key_prefix != NULL)
+ *key_r = t_strconcat(key_prefix, *key_r, NULL);
+
+ /* skip over dovecot's internal attributes. (server metadata is handled
+ inside the private metadata.) */
+ return (imtrans->server ||
+ strncmp(*key_r, MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT,
+ strlen(MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT)) != 0);
+}
+
+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, 0);
+ 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 ?
+ 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;
+
+ memset(&value, 0, sizeof(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;
+
+ memset(value_r, 0, sizeof(*value_r));
+ if (!imap_metadata_entry2key(imtrans, entry, &type, &key))
+ return 0;
+ if (imap_metadata_get_mailbox_transaction(imtrans) < 0)
+ return -1;
+ if (mailbox_attribute_get(imtrans->trans, type, key, value_r) < 0)
+ return -1;
+ return 1;
+}
+
+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;
+
+ memset(value_r, 0, sizeof(*value_r));
+ if (!imap_metadata_entry2key(imtrans, entry, &type, &key))
+ return 0;
+ if (imap_metadata_get_mailbox_transaction(imtrans) < 0)
+ return -1;
+ if (mailbox_attribute_get_stream(imtrans->trans, type, key, value_r) < 0)
+ return -1;
+ return 1;
+}
+
+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);
+ box = mailbox_alloc(ns->list, "INBOX", 0);
+ imtrans = imap_metadata_transaction_begin(box);
+ imtrans->server = TRUE;
+ return imtrans;
+}
+
+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 **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 (error_r != NULL)
+ *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);
+}
--- /dev/null
+#ifndef IMAP_METADATA_H
+#define IMAP_METADATA_H
+
+#define IMAP_METADATA_PRIVATE_PREFIX "/private"
+#define IMAP_METADATA_SHARED_PREFIX "/shared"
+
+struct imap_metadata_iter;
+struct imap_metadata_transaction;
+
+/* Checks whether IMAP metadata entry name is valid */
+bool imap_metadata_verify_entry_name(
+ const char *name, const char **error_r);
+
+/* Set IMAP metadata entry to value. */
+int imap_metadata_set(struct imap_metadata_transaction *imtrans,
+ const char *entry, const struct mail_attribute_value *value);
+/* Delete IMAP metadata entry. This is just a wrapper to
+ imap_metadata_set() with value->value=NULL. */
+int imap_metadata_unset(struct imap_metadata_transaction *imtrans,
+ const char *entry);
+/* Returns value for IMAP metadata entry. Returns 1 if value was returned,
+ 0 if value wasn't found (set to NULL), -1 if error */
+int imap_metadata_get(struct imap_metadata_transaction *imtrans,
+ const char *entry, struct mail_attribute_value *value_r);
+/* Same as imap_metadata_get(), but the returned value may be either an
+ input stream or a string. */
+int imap_metadata_get_stream(struct imap_metadata_transaction *imtrans,
+ const char *entry, struct mail_attribute_value *value_r);
+
+/* Iterate through IMAP metadata entries names under the specified entry. */
+struct imap_metadata_iter *
+imap_metadata_iter_init(struct imap_metadata_transaction *imtrans,
+ const char *entry);
+/* Returns the next IMAP metadata entry name or NULL if there are no more
+ entries. */
+const char *imap_metadata_iter_next(struct imap_metadata_iter *iter);
+int imap_metadata_iter_deinit(struct imap_metadata_iter **_iter);
+
+struct imap_metadata_transaction *
+imap_metadata_transaction_begin(struct mailbox *box);
+struct imap_metadata_transaction *
+imap_metadata_transaction_begin_mailbox(struct mail_user *user,
+ const char *mailbox);
+struct imap_metadata_transaction *
+imap_metadata_transaction_begin_server(struct mail_user *user);
+
+int imap_metadata_transaction_commit(
+ struct imap_metadata_transaction **_imtrans,
+ enum mail_error *error_code_r, const char **error_r);
+void imap_metadata_transaction_rollback(
+ struct imap_metadata_transaction **_imtrans);
+const char *
+imap_metadata_transaction_get_last_error(
+ struct imap_metadata_transaction *imtrans,
+ enum mail_error *error_code_r);
+
+#endif