Mercurial > dovecot > core-2.2
changeset 9813:d1cdc927ea3c HEAD
Reimplemented mail-log plugin using notify plugin framework.
Patch by Mark Washenberger / Rackspace.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Mon, 24 Aug 2009 16:58:53 -0400 |
parents | f751c23561f9 |
children | 830958d044ec |
files | src/plugins/mail-log/Makefile.am src/plugins/mail-log/mail-log-plugin.c |
diffstat | 2 files changed, 294 insertions(+), 586 deletions(-) [+] |
line wrap: on
line diff
--- a/src/plugins/mail-log/Makefile.am Mon Aug 24 16:57:50 2009 -0400 +++ b/src/plugins/mail-log/Makefile.am Mon Aug 24 16:58:53 2009 -0400 @@ -5,7 +5,8 @@ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-storage \ -I$(top_srcdir)/src/lib-storage/index \ - -I$(top_srcdir)/src/lib-storage/index/maildir + -I$(top_srcdir)/src/lib-storage/index/maildir \ + -I$(top_srcdir)/src/plugins/notify lib20_mail_log_plugin_la_LDFLAGS = -module -avoid-version
--- a/src/plugins/mail-log/mail-log-plugin.c Mon Aug 24 16:57:50 2009 -0400 +++ b/src/plugins/mail-log/mail-log-plugin.c Mon Aug 24 16:58:53 2009 -0400 @@ -7,7 +7,7 @@ #include "imap-util.h" #include "mail-storage-private.h" #include "mailbox-list-private.h" -#include "mail-user.h" +#include "notify-plugin.h" #include "mail-log-plugin.h" #include <stdlib.h> @@ -21,8 +21,6 @@ MODULE_CONTEXT(obj, mail_log_mail_module) #define MAIL_LOG_LIST_CONTEXT(obj) \ MODULE_CONTEXT(obj, mail_log_mailbox_list_module) -#define MAIL_LOG_USER_CONTEXT(obj) \ - MODULE_CONTEXT(obj, mail_log_mail_user_module) enum mail_log_field { MAIL_LOG_FIELD_UID = 0x01, @@ -42,15 +40,14 @@ MAIL_LOG_EVENT_DELETE = 0x01, MAIL_LOG_EVENT_UNDELETE = 0x02, MAIL_LOG_EVENT_EXPUNGE = 0x04, - MAIL_LOG_EVENT_COPY = 0x08, + MAIL_LOG_EVENT_SAVE = 0x08, MAIL_LOG_EVENT_MAILBOX_DELETE = 0x10, MAIL_LOG_EVENT_MAILBOX_RENAME = 0x20, - MAIL_LOG_EVENT_FLAG_CHANGE = 0x40, - MAIL_LOG_EVENT_APPEND = 0x80 + MAIL_LOG_EVENT_FLAG_CHANGE = 0x40 }; #define MAIL_LOG_DEFAULT_EVENTS \ (MAIL_LOG_EVENT_DELETE | MAIL_LOG_EVENT_UNDELETE | \ - MAIL_LOG_EVENT_EXPUNGE | MAIL_LOG_EVENT_COPY | \ + MAIL_LOG_EVENT_EXPUNGE | MAIL_LOG_EVENT_SAVE | \ MAIL_LOG_EVENT_MAILBOX_DELETE | MAIL_LOG_EVENT_MAILBOX_RENAME) static const char *field_names[] = { @@ -69,56 +66,30 @@ "delete", "undelete", "expunge", - "copy", + "save", "mailbox_delete", "mailbox_rename", "flag_change", - "append", NULL }; -struct mail_log_user { - union mail_user_module_context module_ctx; - +struct mail_log_settings { enum mail_log_field fields; enum mail_log_event events; - - unsigned int group_events:1; -}; - -struct mail_log_group_changes { - enum mail_log_event event; - const char *data; - - ARRAY_TYPE(seq_range) uids; - uoff_t psize_total, vsize_total; }; -struct mail_log_transaction_context { - union mailbox_transaction_module_context module_ctx; - pool_t pool; - struct mail *tmp_mail; - ARRAY_DEFINE(group_changes, struct mail_log_group_changes); - - unsigned int changes; +struct mail_log_message { + struct mail_log_message *prev, *next; + const char *pretext, *text; }; -const char *mail_log_plugin_version = PACKAGE_VERSION; - -static void (*mail_log_next_hook_mail_storage_created) - (struct mail_storage *storage); -static void (*mail_log_next_hook_mailbox_list_created) - (struct mailbox_list *list); -static void (*mail_log_next_hook_mail_user_created)(struct mail_user *user); +struct mail_log_mail_txn_context { + pool_t pool; + struct mail_log_message *messages, *messages_tail; +}; -static MODULE_CONTEXT_DEFINE_INIT(mail_log_storage_module, - &mail_storage_module_register); -static MODULE_CONTEXT_DEFINE_INIT(mail_log_mail_module, &mail_module_register); -static MODULE_CONTEXT_DEFINE_INIT(mail_log_mailbox_list_module, - &mailbox_list_module_register); -static MODULE_CONTEXT_DEFINE_INIT(mail_log_mail_user_module, - &mail_user_module_register); +static struct mail_log_settings mail_log_set; static enum mail_log_field mail_log_field_find(const char *name) { @@ -142,584 +113,320 @@ return 0; } -static const char *mail_log_event_get_name(enum mail_log_event event) -{ - unsigned int i; - - for (i = 0; event_names[i] != NULL; i++) { - if ((unsigned)event == (unsigned)(1 << i)) - return event_names[i]; - } - i_unreached(); - return NULL; -} - -static struct mail_log_group_changes * -mail_log_action_get_group(struct mail_log_transaction_context *lt, - enum mail_log_event event, const char *data) -{ - struct mail_log_group_changes *group; - unsigned int i, count; - - if (!array_is_created(<->group_changes)) - p_array_init(<->group_changes, lt->pool, 8); - - group = array_get_modifiable(<->group_changes, &count); - for (i = 0; i < count; i++) { - if (group[i].event == event && - null_strcmp(data, group[i].data) == 0) - return &group[i]; - } - - group = array_append_space(<->group_changes); - group->event = event; - group->data = p_strdup(lt->pool, data); - return group; -} - -static void -mail_log_action_add_group(struct mail_log_transaction_context *lt, - struct mail *mail, enum mail_log_event event, - const char *data) -{ - struct mail_log_user *muser = - MAIL_LOG_USER_CONTEXT(mail->box->storage->user); - struct mail_log_group_changes *group; - uoff_t size; - - group = mail_log_action_get_group(lt, event, data); - - if ((muser->fields & MAIL_LOG_FIELD_UID) != 0) { - if (!array_is_created(&group->uids)) - p_array_init(&group->uids, lt->pool, 32); - seq_range_array_add(&group->uids, 0, mail->uid); - } - - if ((muser->fields & MAIL_LOG_FIELD_PSIZE) != 0 && - (event & (MAIL_LOG_EVENT_EXPUNGE | MAIL_LOG_EVENT_COPY)) != 0) { - if (mail_get_physical_size(mail, &size) == 0) - group->psize_total += size; - } - - if ((muser->fields & MAIL_LOG_FIELD_VSIZE) != 0 && - (event & (MAIL_LOG_EVENT_EXPUNGE | MAIL_LOG_EVENT_COPY)) != 0) { - if (mail_get_virtual_size(mail, &size) == 0) - group->vsize_total += size; - } -} - -static void mail_log_append_mailbox_name(string_t *str, struct mailbox *box) -{ - const char *mailbox_str; - - /* most operations are for INBOX, and POP3 has only INBOX, - so don't add it. */ - mailbox_str = mailbox_get_vname(box); - if (strcmp(mailbox_str, "INBOX") != 0) { - str_printfa(str, "box=%s, ", - str_sanitize(mailbox_str, MAILBOX_NAME_LOG_LEN)); - } -} - -static void -mail_log_group(struct mailbox *box, const struct mail_log_group_changes *group) -{ - struct mail_log_user *muser = - MAIL_LOG_USER_CONTEXT(box->storage->user); - const struct seq_range *range; - unsigned int i, count; - string_t *str; - - str = t_str_new(128); - str_printfa(str, "%s: ", mail_log_event_get_name(group->event)); - - if ((muser->fields & MAIL_LOG_FIELD_UID) != 0 && - array_is_created(&group->uids)) { - str_append(str, "uids="); - - range = array_get(&group->uids, &count); - for (i = 0; i < count; i++) { - if (i != 0) - str_append_c(str, ','); - - str_printfa(str, "%u", range[i].seq1); - if (range[i].seq1 != range[i].seq2) - str_printfa(str, "-%u", range[i].seq2); - } - str_append(str, ", "); - } - - if ((muser->fields & MAIL_LOG_FIELD_BOX) != 0) - mail_log_append_mailbox_name(str, box); - - if (group->event == MAIL_LOG_EVENT_COPY) - str_printfa(str, "dest=%s, ", group->data); - - if (group->psize_total != 0) - str_printfa(str, "size=%"PRIuUOFF_T", ", group->psize_total); - if (group->vsize_total != 0) - str_printfa(str, "size=%"PRIuUOFF_T", ", group->vsize_total); - str_truncate(str, str_len(str)-2); - - i_info("%s", str_c(str)); -} - -static void -mail_log_group_changes(struct mailbox *box, - struct mail_log_transaction_context *lt) -{ - const struct mail_log_group_changes *group; - unsigned int i, count; - - group = array_get(<->group_changes, &count); - for (i = 0; i < count; i++) { - T_BEGIN { - mail_log_group(box, &group[i]); - } T_END; - } -} - -static void mail_log_add_hdr(struct mail *mail, string_t *str, - const char *name, const char *header) -{ - const char *value; - - if (mail_get_first_header(mail, header, &value) <= 0) - value = ""; - str_printfa(str, "%s=%s, ", name, str_sanitize(value, HEADER_LOG_LEN)); -} - -static void mail_log_action(struct mailbox_transaction_context *dest_trans, - struct mail *mail, enum mail_log_event event, - const char *data) -{ - struct mail_log_transaction_context *lt = MAIL_LOG_CONTEXT(dest_trans); - struct mail_log_user *muser = - MAIL_LOG_USER_CONTEXT(mail->box->storage->user); - uoff_t size; - string_t *str; - - if ((muser->events & event) == 0) - return; - - lt->changes++; - - if (muser->group_events) { - mail_log_action_add_group(lt, mail, event, data); - return; - } - - str = t_str_new(128); - str_printfa(str, "%s: ", mail_log_event_get_name(event)); - - if ((muser->fields & MAIL_LOG_FIELD_UID) != 0 && mail->uid != 0) - str_printfa(str, "uid=%u, ", mail->uid); - - if ((muser->fields & MAIL_LOG_FIELD_BOX) != 0) - mail_log_append_mailbox_name(str, mail->box); - if ((muser->fields & MAIL_LOG_FIELD_FLAGS) != 0) { - str_printfa(str, "flags=("); - imap_write_flags(str, mail_get_flags(mail), - mail_get_keywords(mail)); - str_append(str, "), "); - } - if (event == MAIL_LOG_EVENT_COPY) - str_printfa(str, "dest=%s, ", data); - - if ((muser->fields & MAIL_LOG_FIELD_MSGID) != 0) - mail_log_add_hdr(mail, str, "msgid", "Message-ID"); - if ((muser->fields & MAIL_LOG_FIELD_FROM) != 0) - mail_log_add_hdr(mail, str, "from", "From"); - if ((muser->fields & MAIL_LOG_FIELD_SUBJECT) != 0) - mail_log_add_hdr(mail, str, "subject", "Subject"); - - if ((muser->fields & MAIL_LOG_FIELD_PSIZE) != 0 && - (event & (MAIL_LOG_EVENT_EXPUNGE | MAIL_LOG_EVENT_COPY)) != 0) { - if (mail_get_physical_size(mail, &size) == 0) - str_printfa(str, "size=%"PRIuUOFF_T", ", size); - } - if ((muser->fields & MAIL_LOG_FIELD_VSIZE) != 0 && - (event & (MAIL_LOG_EVENT_EXPUNGE | MAIL_LOG_EVENT_COPY)) != 0) { - if (mail_get_virtual_size(mail, &size) == 0) - str_printfa(str, "vsize=%"PRIuUOFF_T", ", size); - } - str_truncate(str, str_len(str)-2); - - i_info("%s", str_c(str)); -} - -static void mail_log_mail_expunge(struct mail *_mail) -{ - struct mail_private *mail = (struct mail_private *)_mail; - union mail_module_context *lmail = MAIL_LOG_MAIL_CONTEXT(mail); - - T_BEGIN { - mail_log_action(_mail->transaction, _mail, - MAIL_LOG_EVENT_EXPUNGE, NULL); - } T_END; - lmail->super.expunge(_mail); -} - -static void -mail_log_mail_update_flags(struct mail *_mail, enum modify_type modify_type, - enum mail_flags flags) -{ - struct mail_private *mail = (struct mail_private *)_mail; - union mail_module_context *lmail = MAIL_LOG_MAIL_CONTEXT(mail); - enum mail_flags old_flags, new_flags; - - old_flags = mail_get_flags(_mail); - lmail->super.update_flags(_mail, modify_type, flags); - - new_flags = old_flags; - switch (modify_type) { - case MODIFY_ADD: - new_flags |= flags; - break; - case MODIFY_REMOVE: - new_flags &= ~flags; - break; - case MODIFY_REPLACE: - new_flags = flags; - break; - } - - if (((old_flags ^ new_flags) & MAIL_DELETED) != 0) T_BEGIN { - mail_log_action(_mail->transaction, _mail, - (new_flags & MAIL_DELETED) != 0 ? - MAIL_LOG_EVENT_DELETE : - MAIL_LOG_EVENT_UNDELETE, NULL); - } T_END; - - if ((old_flags & ~MAIL_DELETED) != (new_flags & ~MAIL_DELETED)) { - mail_log_action(_mail->transaction, _mail, - MAIL_LOG_EVENT_FLAG_CHANGE, NULL); - } -} - -static void -mail_log_mail_update_keywords(struct mail *_mail, enum modify_type modify_type, - struct mail_keywords *keywords) -{ - struct mail_private *mail = (struct mail_private *)_mail; - union mail_module_context *lmail = MAIL_LOG_MAIL_CONTEXT(mail); - const char *const *old_keywords, *const *new_keywords; - unsigned int i; - - old_keywords = mail_get_keywords(_mail); - lmail->super.update_keywords(_mail, modify_type, keywords); - new_keywords = mail_get_keywords(_mail); - - for (i = 0; old_keywords[i] != NULL && new_keywords[i] != NULL; i++) { - if (strcmp(old_keywords[i], new_keywords[i]) != 0) - break; - } - - if (old_keywords[i] != NULL || new_keywords[i] != NULL) { - mail_log_action(_mail->transaction, _mail, - MAIL_LOG_EVENT_FLAG_CHANGE, NULL); - } -} - -static struct mail * -mail_log_mail_alloc(struct mailbox_transaction_context *t, - enum mail_fetch_field wanted_fields, - struct mailbox_header_lookup_ctx *wanted_headers) -{ - union mailbox_module_context *lbox = MAIL_LOG_CONTEXT(t->box); - union mail_module_context *lmail; - struct mail *_mail; - struct mail_private *mail; - - _mail = lbox->super.mail_alloc(t, wanted_fields, wanted_headers); - mail = (struct mail_private *)_mail; - - lmail = p_new(mail->pool, union mail_module_context, 1); - lmail->super = mail->v; - - mail->v.update_flags = mail_log_mail_update_flags; - mail->v.update_keywords = mail_log_mail_update_keywords; - mail->v.expunge = mail_log_mail_expunge; - MODULE_CONTEXT_SET_SELF(mail, mail_log_mail_module, lmail); - return _mail; -} - -static int -mail_log_copy(struct mail_save_context *ctx, struct mail *mail) -{ - union mailbox_module_context *lbox = - MAIL_LOG_CONTEXT(ctx->transaction->box); - const char *name; - - if (lbox->super.copy(ctx, mail) < 0) - return -1; - - T_BEGIN { - name = str_sanitize(mailbox_get_vname(ctx->transaction->box), - MAILBOX_NAME_LOG_LEN); - mail_log_action(ctx->transaction, mail, - MAIL_LOG_EVENT_COPY, name); - } T_END; - return 0; -} - -static int -mail_log_save_begin(struct mail_save_context *ctx, struct istream *input) -{ - struct mail_log_transaction_context *lt = - MAIL_LOG_CONTEXT(ctx->transaction); - union mailbox_module_context *lbox = - MAIL_LOG_CONTEXT(ctx->transaction->box); - - if (ctx->dest_mail == NULL) { - if (lt->tmp_mail == NULL) - lt->tmp_mail = mail_alloc(ctx->transaction, 0, NULL); - ctx->dest_mail = lt->tmp_mail; - } - - return lbox->super.save_begin(ctx, input); -} - -static int mail_log_save_finish(struct mail_save_context *ctx) -{ - union mailbox_module_context *lbox = - MAIL_LOG_CONTEXT(ctx->transaction->box); - - if (lbox->super.save_finish(ctx) < 0) - return -1; - - T_BEGIN { - mail_log_action(ctx->transaction, ctx->dest_mail, - MAIL_LOG_EVENT_APPEND, NULL); - } T_END; - return 0; -} - -static struct mailbox_transaction_context * -mail_log_transaction_begin(struct mailbox *box, - enum mailbox_transaction_flags flags) -{ - union mailbox_module_context *lbox = MAIL_LOG_CONTEXT(box); - struct mailbox_transaction_context *t; - struct mail_log_transaction_context *lt; - pool_t pool; - - t = lbox->super.transaction_begin(box, flags); - - pool = pool_alloconly_create("mail log transaction", 1024); - lt = p_new(pool, struct mail_log_transaction_context, 1); - lt->pool = pool; - MODULE_CONTEXT_SET(t, mail_log_storage_module, lt); - return t; -} - -static int -mail_log_transaction_commit(struct mailbox_transaction_context *t, - struct mail_transaction_commit_changes *changes_r) -{ - struct mail_log_transaction_context *lt = MAIL_LOG_CONTEXT(t); - union mailbox_module_context *lbox = MAIL_LOG_CONTEXT(t->box); - struct mail_log_user *muser = - MAIL_LOG_USER_CONTEXT(t->box->storage->user); - - if (lt->changes > 0 && muser->group_events) - mail_log_group_changes(t->box, lt); - if (lt->tmp_mail != NULL) - mail_free(<->tmp_mail); - pool_unref(<->pool); - - return lbox->super.transaction_commit(t, changes_r); -} - -static void -mail_log_transaction_rollback(struct mailbox_transaction_context *t) -{ - struct mail_log_transaction_context *lt = MAIL_LOG_CONTEXT(t); - union mailbox_module_context *lbox = MAIL_LOG_CONTEXT(t->box); - struct mail_log_user *muser = - MAIL_LOG_USER_CONTEXT(t->box->storage->user); - - if (lt->changes > 0 && !muser->group_events) { - i_info("Transaction rolled back: " - "Ignore last %u changes", lt->changes); - } - if (lt->tmp_mail != NULL) - mail_free(<->tmp_mail); - pool_unref(<->pool); - - lbox->super.transaction_rollback(t); -} - -static struct mailbox * -mail_log_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list, - const char *name, struct istream *input, - enum mailbox_flags flags) -{ - union mail_storage_module_context *lstorage = MAIL_LOG_CONTEXT(storage); - struct mailbox *box; - union mailbox_module_context *lbox; - - box = lstorage->super.mailbox_alloc(storage, list, name, input, flags); - - lbox = p_new(box->pool, union mailbox_module_context, 1); - lbox->super = box->v; - - box->v.mail_alloc = mail_log_mail_alloc; - box->v.copy = mail_log_copy; - box->v.save_begin = mail_log_save_begin; - box->v.save_finish = mail_log_save_finish; - box->v.transaction_begin = mail_log_transaction_begin; - box->v.transaction_commit = mail_log_transaction_commit; - box->v.transaction_rollback = mail_log_transaction_rollback; - MODULE_CONTEXT_SET_SELF(box, mail_log_storage_module, lbox); - return box; -} - -static int -mail_log_mailbox_list_delete(struct mailbox_list *list, const char *name) -{ - union mailbox_list_module_context *llist = MAIL_LOG_LIST_CONTEXT(list); - struct mail_log_user *muser = MAIL_LOG_USER_CONTEXT(list->ns->user); - - if (llist->super.delete_mailbox(list, name) < 0) - return -1; - - if ((muser->events & MAIL_LOG_EVENT_MAILBOX_DELETE) == 0) - return 0; - - i_info("Mailbox deleted: %s", str_sanitize(name, MAILBOX_NAME_LOG_LEN)); - return 0; -} - -static int -mail_log_mailbox_list_rename(struct mailbox_list *oldlist, const char *oldname, - struct mailbox_list *newlist, const char *newname, - bool rename_children) -{ - union mailbox_list_module_context *llist = - MAIL_LOG_LIST_CONTEXT(oldlist); - struct mail_log_user *muser = MAIL_LOG_USER_CONTEXT(oldlist->ns->user); - - if (llist->super.rename_mailbox(oldlist, oldname, newlist, newname, - rename_children) < 0) - return -1; - - if ((muser->events & MAIL_LOG_EVENT_MAILBOX_RENAME) == 0) - return 0; - - i_info("Mailbox renamed: %s -> %s", - str_sanitize(oldname, MAILBOX_NAME_LOG_LEN), - str_sanitize(newname, MAILBOX_NAME_LOG_LEN)); - return 0; -} - -static void mail_log_mail_storage_created(struct mail_storage *storage) -{ - union mail_storage_module_context *lstorage; - - lstorage = p_new(storage->pool, union mail_storage_module_context, 1); - lstorage->super = storage->v; - storage->v.mailbox_alloc = mail_log_mailbox_alloc; - - MODULE_CONTEXT_SET_SELF(storage, mail_log_storage_module, lstorage); - - if (mail_log_next_hook_mail_storage_created != NULL) - mail_log_next_hook_mail_storage_created(storage); -} - -static void mail_log_mailbox_list_created(struct mailbox_list *list) -{ - union mailbox_list_module_context *llist; - - llist = p_new(list->pool, union mailbox_list_module_context, 1); - llist->super = list->v; - list->v.delete_mailbox = mail_log_mailbox_list_delete; - list->v.rename_mailbox = mail_log_mailbox_list_rename; - - MODULE_CONTEXT_SET_SELF(list, mail_log_mailbox_list_module, llist); - - if (mail_log_next_hook_mailbox_list_created != NULL) - mail_log_next_hook_mailbox_list_created(list); -} - -static int mail_log_parse_fields(const char *str, enum mail_log_field *fields_r) +static enum mail_log_field mail_log_parse_fields(const char *str) { const char *const *tmp; static enum mail_log_field field, fields = 0; for (tmp = t_strsplit_spaces(str, ", "); *tmp != NULL; tmp++) { field = mail_log_field_find(*tmp); - if (field == 0) { - i_error("Unknown field in mail_log_fields: '%s'", *tmp); - return -1; - } + if (field == 0) + i_fatal("Unknown field in mail_log_fields: '%s'", *tmp); fields |= field; } - *fields_r = fields; - return 0; + return fields; } -static int mail_log_parse_events(const char *str, enum mail_log_event *events_r) +static enum mail_log_event mail_log_parse_events(const char *str) { const char *const *tmp; static enum mail_log_event event, events = 0; for (tmp = t_strsplit_spaces(str, ", "); *tmp != NULL; tmp++) { event = mail_log_event_find(*tmp); - if (event == 0) { - i_error("Unknown event in mail_log_events: '%s'", *tmp); - return -1; - } + if (event == 0) + i_fatal("Unknown event in mail_log_events: '%s'", *tmp); events |= event; } - *events_r = events; - return 0; + return events; } -static void -mail_log_read_settings(struct mail_user *user, struct mail_log_user *muser) +static void mail_log_read_settings(struct mail_log_settings *set) { const char *str; - str = mail_user_plugin_getenv(user, "mail_log_fields"); - if (str == NULL || mail_log_parse_fields(str, &muser->fields) < 0) - muser->fields = MAIL_LOG_DEFAULT_FIELDS; + memset(set, 0, sizeof(*set)); + + str = getenv("MAIL_LOG_FIELDS"); + set->fields = str == NULL ? MAIL_LOG_DEFAULT_FIELDS : + mail_log_parse_fields(str); + + str = getenv("MAIL_LOG_EVENTS"); + set->events = str == NULL ? MAIL_LOG_DEFAULT_EVENTS : + mail_log_parse_events(str); +} + +static void mail_log_append_mailbox_name(string_t *str, struct mail *mail) +{ + const char *mailbox_str; + + mailbox_str = mailbox_get_name(mail->box); + str_printfa(str, "box=%s", + str_sanitize(mailbox_str, MAILBOX_NAME_LOG_LEN)); +} + +static void +mail_log_append_mail_header(string_t *str, struct mail *mail, + const char *name, const char *header) +{ + const char *value; + + if (mail_get_first_header(mail, header, &value) <= 0) + value = ""; + str_printfa(str, "%s=%s", name, str_sanitize(value, HEADER_LOG_LEN)); +} + +static void +mail_log_append_uid(struct mail_log_mail_txn_context *ctx, + struct mail_log_message *msg, string_t *str, uint32_t uid) +{ + if (uid != 0) + str_printfa(str, "uid=%u", uid); + else { + /* we don't know the uid yet, assign it later */ + str_printfa(str, "uid="); + msg->pretext = p_strdup(ctx->pool, str_c(str)); + str_truncate(str, 0); + } +} + +static void +mail_log_append_mail_message_real(struct mail_log_mail_txn_context *ctx, + struct mail *mail, enum mail_log_event event, + const char *desc) +{ + struct mail_log_message *msg; + string_t *text; + uoff_t size; + + msg = p_new(ctx->pool, struct mail_log_message, 1); - str = mail_user_plugin_getenv(user, "mail_log_events"); - if (str == NULL || mail_log_parse_events(str, &muser->events) < 0) - muser->events = MAIL_LOG_DEFAULT_EVENTS; + text = t_str_new(128); + str_append(text, desc); + str_append(text, ": "); + if ((mail_log_set.fields & MAIL_LOG_FIELD_BOX) != 0) { + mail_log_append_mailbox_name(text, mail); + str_append(text, ", "); + } + if ((mail_log_set.fields & MAIL_LOG_FIELD_UID) != 0) { + if (event == MAIL_LOG_EVENT_SAVE) + mail_log_append_uid(ctx, msg, text, 0); + else + mail_log_append_uid(ctx, msg, text, mail->uid); + str_append(text, ", "); + } + if ((mail_log_set.fields & MAIL_LOG_FIELD_MSGID) != 0) { + mail_log_append_mail_header(text, mail, "msgid", "Message-ID"); + str_append(text, ", "); + } + if ((mail_log_set.fields & MAIL_LOG_FIELD_PSIZE) != 0) { + if (mail_get_physical_size(mail, &size) == 0) + str_printfa(text, "size=%"PRIuUOFF_T, size); + else + str_printfa(text, "size=error"); + str_append(text, ", "); + } + if ((mail_log_set.fields & MAIL_LOG_FIELD_VSIZE) != 0) { + if (mail_get_virtual_size(mail, &size) == 0) + str_printfa(text, "vsize=%"PRIuUOFF_T, size); + else + str_printfa(text, "vsize=error"); + str_append(text, ", "); + } + if ((mail_log_set.fields & MAIL_LOG_FIELD_FROM) != 0) { + mail_log_append_mail_header(text, mail, "from", "From"); + str_append(text, ", "); + } + if ((mail_log_set.fields & MAIL_LOG_FIELD_SUBJECT) != 0) { + mail_log_append_mail_header(text, mail, "subject", "Subject"); + str_append(text, ", "); + } + if ((mail_log_set.fields & MAIL_LOG_FIELD_FLAGS) != 0) { + str_printfa(text, "flags=("); + imap_write_flags(text, mail_get_flags(mail), + mail_get_keywords(mail)); + str_append(text, "), "); + } + str_truncate(text, str_len(text)-2); - muser->group_events = - mail_user_plugin_getenv(user, "mail_log_group_events") != NULL; + msg->text = p_strdup(ctx->pool, str_c(text)); + msg->prev = ctx->messages_tail; + ctx->messages_tail = msg; + if (msg->prev != NULL) + msg->prev->next = msg; + if (ctx->messages == NULL) + ctx->messages = msg; +} + +static void +mail_log_append_mail_message(struct mail_log_mail_txn_context *ctx, + struct mail *mail, enum mail_log_event event, + const char *desc) +{ + if ((mail_log_set.events & event) == 0) + return; + + T_BEGIN { + mail_log_append_mail_message_real(ctx, mail, event, desc); + } T_END; } -static void mail_log_mail_user_created(struct mail_user *user) +static void * +mail_log_mail_transaction_begin(struct mailbox_transaction_context *t ATTR_UNUSED) +{ + pool_t pool; + struct mail_log_mail_txn_context *ctx; + + pool = pool_alloconly_create("mail-log", 1024); + ctx = p_new(pool, struct mail_log_mail_txn_context, 1); + ctx->pool = pool; + return ctx; +} + +static void mail_log_mail_save(void *txn, struct mail *mail) +{ + struct mail_log_mail_txn_context *ctx = + (struct mail_log_mail_txn_context *)txn; + + mail_log_append_mail_message(ctx, mail, MAIL_LOG_EVENT_SAVE, "save"); +} + +static void mail_log_mail_copy(void *txn, struct mail *src, struct mail *dst) +{ + struct mail_log_mail_txn_context *ctx = + (struct mail_log_mail_txn_context *)txn; + const char *desc; + + desc = t_strdup_printf("copy from %s", + str_sanitize(mailbox_get_name(src->box), + MAILBOX_NAME_LOG_LEN)); + mail_log_append_mail_message(ctx, dst, MAIL_LOG_EVENT_SAVE, desc); +} + +static void mail_log_mail_expunge(void *txn, struct mail *mail) { - struct mail_log_user *muser; + struct mail_log_mail_txn_context *ctx = + (struct mail_log_mail_txn_context *)txn; + + mail_log_append_mail_message(ctx, mail, MAIL_LOG_EVENT_EXPUNGE, + "expunge"); +} + +static void mail_log_mail_update_flags(void *txn, struct mail *mail, + enum mail_flags old_flags) +{ + struct mail_log_mail_txn_context *ctx = + (struct mail_log_mail_txn_context *)txn; + enum mail_flags new_flags = mail_get_flags(mail); + + if (((old_flags ^ new_flags) & MAIL_DELETED) == 0) { + mail_log_append_mail_message(ctx, mail, + MAIL_LOG_EVENT_FLAG_CHANGE, + "flag_change"); + } else if ((old_flags & MAIL_DELETED) == 0) { + mail_log_append_mail_message(ctx, mail, MAIL_LOG_EVENT_DELETE, + "delete"); + } else { + mail_log_append_mail_message(ctx, mail, MAIL_LOG_EVENT_UNDELETE, + "undelete"); + } +} + +static void +mail_log_mail_update_keywords(void *txn, struct mail *mail, + const char *const *old_keywords ATTR_UNUSED) +{ + struct mail_log_mail_txn_context *ctx = + (struct mail_log_mail_txn_context *)txn; + + mail_log_append_mail_message(ctx, mail, MAIL_LOG_EVENT_FLAG_CHANGE, + "flag_change"); +} - muser = p_new(user->pool, struct mail_log_user, 1); - mail_log_read_settings(user, muser); - MODULE_CONTEXT_SET(user, mail_log_mail_user_module, muser); +static void +mail_log_mail_transaction_commit(void *txn, + struct mail_transaction_commit_changes *changes) +{ + struct mail_log_mail_txn_context *ctx = + (struct mail_log_mail_txn_context *)txn; + struct mail_log_message *msg; + struct seq_range_iter iter; + unsigned int n = 0; + uint32_t uid; + bool ret; + + seq_range_array_iter_init(&iter, &changes->saved_uids); + for (msg = ctx->messages; msg != NULL; msg = msg->next) { + if (msg->pretext == NULL) { + i_info("%s", msg->text); + } else { + ret = seq_range_array_iter_nth(&iter, n++, &uid); + i_assert(ret); + i_info("%s%u%s", msg->pretext, uid, msg->text); + } + } + i_assert(!seq_range_array_iter_nth(&iter, n, &uid)); + + pool_unref(&ctx->pool); +} + +static void mail_log_mail_transaction_rollback(void *txn) +{ + struct mail_log_mail_txn_context *ctx = + (struct mail_log_mail_txn_context *)txn; + + pool_unref(&ctx->pool); +} - if (mail_log_next_hook_mail_user_created != NULL) - mail_log_next_hook_mail_user_created(user); +static void +mail_log_mailbox_delete_commit(void *txn ATTR_UNUSED, + struct mailbox_list *list ATTR_UNUSED, + const char *name) +{ + if ((mail_log_set.events & MAIL_LOG_EVENT_MAILBOX_DELETE) == 0) + return; + + i_info("Mailbox deleted: %s", str_sanitize(name, MAILBOX_NAME_LOG_LEN)); } +static void +mail_log_mailbox_rename(struct mailbox_list *oldlist ATTR_UNUSED, + const char *oldname, + struct mailbox_list *newlist ATTR_UNUSED, + const char *newname, bool rename_children ATTR_UNUSED) +{ + if ((mail_log_set.events & MAIL_LOG_EVENT_MAILBOX_RENAME) == 0) + return; + + i_info("Mailbox renamed: %s -> %s", + str_sanitize(oldname, MAILBOX_NAME_LOG_LEN), + str_sanitize(newname, MAILBOX_NAME_LOG_LEN)); +} + +static const struct notify_vfuncs mail_log_vfuncs = { + /* mail_transaction_begin */ mail_log_mail_transaction_begin, + /* mail_save */ mail_log_mail_save, + /* mail_copy */ mail_log_mail_copy, + /* mail_expunge */ mail_log_mail_expunge, + /* mail_update_flags */ mail_log_mail_update_flags, + /* mail_update_keywords */ mail_log_mail_update_keywords, + /* mail_transaction_commit */ mail_log_mail_transaction_commit, + /* mail_transaction_rollback */ mail_log_mail_transaction_rollback, + /* mailbox_delete_begin */ notify_noop_mailbox_delete_begin, + /* mailbox_delete_commit */ mail_log_mailbox_delete_commit, + /* mailbox_delete_rollback */ notify_noop_mailbox_delete_rollback, + /* mailbox_rename */ mail_log_mailbox_rename, +}; + +static struct notify_context *mail_log_ctx; + void mail_log_plugin_init(void) { - mail_log_next_hook_mail_storage_created = hook_mail_storage_created; - hook_mail_storage_created = mail_log_mail_storage_created; - - mail_log_next_hook_mailbox_list_created = hook_mailbox_list_created; - hook_mailbox_list_created = mail_log_mailbox_list_created; - - mail_log_next_hook_mail_user_created = hook_mail_user_created; - hook_mail_user_created = mail_log_mail_user_created; + mail_log_read_settings(&mail_log_set); + mail_log_ctx = notify_register(&mail_log_vfuncs); } void mail_log_plugin_deinit(void) { - hook_mail_storage_created = mail_log_next_hook_mail_storage_created; - hook_mailbox_list_created = mail_log_next_hook_mailbox_list_created; - hook_mail_user_created = mail_log_next_hook_mail_user_created; + notify_unregister(mail_log_ctx); }