Mercurial > dovecot > original-hg > dovecot-1.2
changeset 5794:ea050869097b HEAD
Added mail_log_events, mail_log_group_events and mail_log_fields settings to
mail_log plugin.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Thu, 21 Jun 2007 00:51:34 +0300 |
parents | 512f8982ae8b |
children | 0c0a829a9a63 |
files | dovecot-example.conf src/plugins/mail-log/mail-log-plugin.c |
diffstat | 2 files changed, 385 insertions(+), 17 deletions(-) [+] |
line wrap: on
line diff
--- a/dovecot-example.conf Wed Jun 20 23:50:38 2007 +0300 +++ b/dovecot-example.conf Thu Jun 21 00:51:34 2007 +0300 @@ -1103,4 +1103,12 @@ # they're moved to a 3rd namespace. The mails won't be counted in quota, # and they're not deleted automatically (use a cronjob or something). #lazy_expunge = .EXPUNGED/ .DELETED/ .DELETED/.EXPUNGED/ + + # Events to log. Default is all. + #mail_log_events = delete undelete expunge copy mailbox_delete + # Group events within a transaction to one line. + #mail_log_group_events = + # Available fields: uid, box, msgid, size, vsize + # size and vsize are available only for expunge and copy events. + #mail_log_fields = uid box msgid size }
--- a/src/plugins/mail-log/mail-log-plugin.c Wed Jun 20 23:50:38 2007 +0300 +++ b/src/plugins/mail-log/mail-log-plugin.c Thu Jun 21 00:51:34 2007 +0300 @@ -2,11 +2,14 @@ #include "lib.h" #include "array.h" +#include "str.h" #include "str-sanitize.h" #include "mail-storage-private.h" #include "mailbox-list-private.h" #include "mail-log-plugin.h" +#include <stdlib.h> + #define MAILBOX_NAME_LOG_LEN 64 #define MSGID_LOG_LEN 80 @@ -17,8 +20,74 @@ #define MAIL_LOG_LIST_CONTEXT(obj) \ MODULE_CONTEXT(obj, mail_log_mailbox_list_module) +enum mail_log_field { + MAIL_LOG_FIELD_UID = 0x01, + MAIL_LOG_FIELD_BOX = 0x02, + MAIL_LOG_FIELD_MSGID = 0x04, + MAIL_LOG_FIELD_PSIZE = 0x08, + MAIL_LOG_FIELD_VSIZE = 0x10 +}; +#define MAIL_LOG_DEFAULT_FIELDS \ + (MAIL_LOG_FIELD_UID | MAIL_LOG_FIELD_BOX | \ + MAIL_LOG_FIELD_MSGID | MAIL_LOG_FIELD_PSIZE) + +enum mail_log_event { + MAIL_LOG_EVENT_DELETE = 0x01, + MAIL_LOG_EVENT_UNDELETE = 0x02, + MAIL_LOG_EVENT_EXPUNGE = 0x04, + MAIL_LOG_EVENT_COPY = 0x08, + MAIL_LOG_EVENT_MAILBOX_DELETE = 0x10, + + MAIL_LOG_EVENT_MASK_ALL = 0x1f +}; +#define MAIL_LOG_DEFAULT_EVENTS MAIL_LOG_EVENT_MASK_ALL + +static const char *field_names[] = { + "uid", + "box", + "msgid", + "size", + "vsize", + NULL +}; + +static const char *event_names[] = { + "delete", + "undelete", + "expunge", + "copy", + "mailbox_delete", + NULL +}; + +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; + + ARRAY_DEFINE(group_changes, struct mail_log_group_changes); + + unsigned int changes; +}; + const char *mail_log_plugin_version = PACKAGE_VERSION; +static struct mail_log_settings mail_log_set; + static void (*mail_log_next_hook_mail_storage_created) (struct mail_storage *storage); static void (*mail_log_next_hook_mailbox_list_created) @@ -30,24 +99,225 @@ static MODULE_CONTEXT_DEFINE_INIT(mail_log_mailbox_list_module, &mailbox_list_module_register); -static void mail_log_action(struct mail *mail, const char *action) +static enum mail_log_field mail_log_field_find(const char *name) +{ + unsigned int i; + + for (i = 0; field_names[i] != NULL; i++) { + if (strcmp(name, field_names[i]) == 0) + return 1 << i; + } + return 0; +} + +static enum mail_log_event mail_log_event_find(const char *name) { - const char *msgid, *mailbox_str; + unsigned int i; + + for (i = 0; event_names[i] != NULL; i++) { + if (strcmp(name, event_names[i]) == 0) + return 1 << i; + } + 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; +} - mailbox_str = mailbox_get_name(mail->box); - if (strcmp(mailbox_str, "INBOX") == 0) { - /* most operations are for INBOX, and POP3 has only INBOX, - so don't add it. */ - mailbox_str = ""; - } else { - mailbox_str = str_sanitize(mailbox_str, 80); - mailbox_str = t_strconcat(", box=", mailbox_str, 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_group_changes *group; + uoff_t size; + + group = mail_log_action_get_group(lt, event, data); + + if ((mail_log_set.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 ((mail_log_set.fields & MAIL_LOG_FIELD_PSIZE) != 0 && + (event & (MAIL_LOG_EVENT_EXPUNGE | MAIL_LOG_EVENT_COPY)) != 0) { + size = mail_get_physical_size(mail); + if (size != (uoff_t)-1) + group->psize_total += size; } - msgid = mail_get_first_header(mail, "Message-ID"); - i_info("%s: uid=%u, msgid=%s%s", action, mail->uid, - msgid == NULL ? "(null)" : str_sanitize(msgid, MSGID_LOG_LEN), - mailbox_str); + if ((mail_log_set.fields & MAIL_LOG_FIELD_VSIZE) != 0 && + (event & (MAIL_LOG_EVENT_EXPUNGE | MAIL_LOG_EVENT_COPY)) != 0) { + size = mail_get_virtual_size(mail); + if (size != (uoff_t)-1) + 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_name(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) +{ + const struct seq_range *range; + unsigned int i, count; + string_t *str; + + t_push(); + + str = t_str_new(128); + str_printfa(str, "%s: ", mail_log_event_get_name(group->event)); + + if ((mail_log_set.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 ((mail_log_set.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)); + t_pop(); +} + +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++) + mail_log_group(box, &group[i]); +} + +static void mail_log_action(struct mail *mail, enum mail_log_event event, + const char *data) +{ + struct mail_log_transaction_context *lt = + MAIL_LOG_CONTEXT(mail->transaction); + const char *msgid; + uoff_t size; + string_t *str; + pool_t pool; + + if ((mail_log_set.events & event) == 0) + return; + + if (lt == NULL) { + pool = pool_alloconly_create("mail log transaction", 1024); + lt = p_new(pool, struct mail_log_transaction_context, 1); + lt->pool = pool; + MODULE_CONTEXT_SET(mail->transaction, + mail_log_storage_module, lt); + } + lt->changes++; + + if (mail_log_set.group_events) { + mail_log_action_add_group(lt, mail, event, data); + return; + } + + t_push(); + str = t_str_new(128); + str_printfa(str, "%s: ", mail_log_event_get_name(event)); + + if ((mail_log_set.fields & MAIL_LOG_FIELD_UID) != 0) + str_printfa(str, "uid=%u, ", mail->uid); + + if ((mail_log_set.fields & MAIL_LOG_FIELD_BOX) != 0) + mail_log_append_mailbox_name(str, mail->box); + + if (event == MAIL_LOG_EVENT_COPY) + str_printfa(str, "dest=%s, ", data); + + if ((mail_log_set.fields & MAIL_LOG_FIELD_MSGID) != 0) { + msgid = mail_get_first_header(mail, "Message-ID"); + str_printfa(str, "msgid=%s, ", msgid == NULL ? "(null)" : + str_sanitize(msgid, MSGID_LOG_LEN)); + } + + if ((mail_log_set.fields & MAIL_LOG_FIELD_PSIZE) != 0 && + (event & (MAIL_LOG_EVENT_EXPUNGE | MAIL_LOG_EVENT_COPY)) != 0) { + size = mail_get_physical_size(mail); + if (size != (uoff_t)-1) + str_printfa(str, "size=%"PRIuUOFF_T", ", size); + } + if ((mail_log_set.fields & MAIL_LOG_FIELD_VSIZE) != 0 && + (event & (MAIL_LOG_EVENT_EXPUNGE | MAIL_LOG_EVENT_COPY)) != 0) { + size = mail_get_virtual_size(mail); + if (size != (uoff_t)-1) + str_printfa(str, "vsize=%"PRIuUOFF_T", ", size); + } + str_truncate(str, str_len(str)-2); + + i_info("%s", str_c(str)); + t_pop(); } static int mail_log_mail_expunge(struct mail *_mail) @@ -58,7 +328,7 @@ if (lmail->super.expunge(_mail) < 0) return -1; - mail_log_action(_mail, "expunged"); + mail_log_action(_mail, MAIL_LOG_EVENT_EXPUNGE, NULL); return 0; } @@ -90,7 +360,7 @@ return 0; mail_log_action(_mail, (new_flags & MAIL_DELETED) != 0 ? - "deleted" : "undeleted"); + MAIL_LOG_EVENT_DELETE : MAIL_LOG_EVENT_UNDELETE, NULL); return 0; } @@ -129,11 +399,47 @@ t_push(); name = str_sanitize(mailbox_get_name(t->box), MAILBOX_NAME_LOG_LEN); - mail_log_action(mail, t_strdup_printf("copy -> %s", name)); + mail_log_action(mail, MAIL_LOG_EVENT_COPY, name); t_pop(); return 0; } +static int +mail_log_transaction_commit(struct mailbox_transaction_context *t, + enum mailbox_sync_flags flags, + uint32_t *first_saved_uid_r, + uint32_t *last_saved_uid_r) +{ + struct mail_log_transaction_context *lt = MAIL_LOG_CONTEXT(t); + union mailbox_module_context *lbox = MAIL_LOG_CONTEXT(t->box); + + if (lt != NULL) { + if (lt->changes > 0 && mail_log_set.group_events) + mail_log_group_changes(t->box, lt); + pool_unref(lt->pool); + } + + return lbox->super.transaction_commit(t, flags, first_saved_uid_r, + last_saved_uid_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); + + if (lt != NULL) { + if (lt->changes > 0 && !mail_log_set.group_events) { + i_info("Transaction rolled back: " + "Ignore last %u changes", lt->changes); + } + pool_unref(lt->pool); + } + + lbox->super.transaction_rollback(t); +} + static struct mailbox * mail_log_mailbox_open(struct mail_storage *storage, const char *name, struct istream *input, enum mailbox_open_flags flags) @@ -151,6 +457,8 @@ box->v.mail_alloc = mail_log_mail_alloc; box->v.copy = mail_log_copy; + 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; } @@ -163,6 +471,9 @@ if (llist->super.delete_mailbox(list, name) < 0) return -1; + if ((mail_log_set.events & MAIL_LOG_EVENT_MAILBOX_DELETE) == 0) + return 0; + i_info("Mailbox deleted: %s", str_sanitize(name, MAILBOX_NAME_LOG_LEN)); return 0; } @@ -195,8 +506,57 @@ MODULE_CONTEXT_SET_SELF(list, mail_log_mailbox_list_module, llist); } +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_fatal("Unknown field in mail_log_fields: '%s'", *tmp); + fields |= field; + } + return fields; +} + +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_fatal("Unknown event in mail_log_events: '%s'", *tmp); + events |= event; + } + return events; +} + +static void mail_log_read_settings(struct mail_log_settings *set) +{ + const char *str; + + memset(set, 0, sizeof(*set)); + + t_push(); + 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); + + set->group_events = getenv("MAIL_LOG_GROUP_EVENTS") != NULL; + t_pop(); +} + void mail_log_plugin_init(void) { + mail_log_read_settings(&mail_log_set); + mail_log_next_hook_mail_storage_created = hook_mail_storage_created; hook_mail_storage_created = mail_log_mail_storage_created;