notify-status-plugin.c revision 7e37e7ac1c561538329625a6cf6fe191a7ed0f10
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi/* Copyright (c) 2017 Dovecot authors, see the included COPYING file */
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi#include "lib.h"
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi#include "array.h"
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi#include "json-parser.h"
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi#include "str.h"
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi#include "var-expand.h"
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi#include "mail-user.h"
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi#include "mail-storage.h"
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi#include "mail-storage-private.h"
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi#include "mail-namespace.h"
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi#include "mail-storage-hooks.h"
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi#include "imap-match.h"
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi#include "dict.h"
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi#include "notify-plugin.h"
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi#define NOTIFY_STATUS_SETTING_DICT_URI "notify_status_dict"
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi#define NOTIFY_STATUS_SETTING_MAILBOX_PREFIX "notify_status_mailbox"
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi#define NOTIFY_STATUS_SETTING_VALUE_TEMPLATE "notify_status_value"
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi#define NOTIFY_STATUS_SETTING_VALUE_TEMPLATE_DEFAULT "{\"messages\":%{messages},\"unseen\":%{unseen}}"
7e37e7ac1c561538329625a6cf6fe191a7ed0f10Timo Sirainen#define NOTIFY_STATUS_KEY "priv/status/%s"
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi#define NOTIFY_STATUS_USER_CONTEXT(obj) \
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi (struct notify_status_user*)MODULE_CONTEXT(obj, notify_status_user_module)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic MODULE_CONTEXT_DEFINE_INIT(notify_status_user_module,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi &mail_user_module_register);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomivoid notify_status_plugin_init(struct module *module);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomivoid notify_status_plugin_deinit(void);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomiconst char *notify_status_plugin_version = DOVECOT_ABI_VERSION;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomiconst char *notify_status_plugin_dependencies[] = { "notify", NULL };
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki TuomiARRAY_DEFINE_TYPE(imap_match_glob, struct imap_match_glob*);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistruct notify_status_mail_txn {
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct mailbox *box;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi bool changed:1;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi};
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistruct notify_status_user {
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi union mail_user_module_context module_ctx;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi ARRAY_TYPE(imap_match_glob) patterns;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct dict *dict;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi const char *value_template;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct notify_context *context;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi};
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic int notify_status_dict_init(struct mail_user *user, const char *uri,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct dict **dict_r, const char **error_r)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct dict_settings set = {
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi .username = user->username,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi .base_dir = user->set->base_dir,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi };
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi (void)mail_user_get_home(user, &set.home_dir);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if (dict_init(uri, &set, dict_r, error_r) < 0) {
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi *error_r = t_strdup_printf("dict_init(%s) failed: %s",
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi uri, *error_r);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi return -1;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi }
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi return 0;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi}
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic void notify_status_mailbox_patterns_init(struct mail_user *user,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi ARRAY_TYPE(imap_match_glob) *patterns)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi const char *value;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi unsigned int i;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi p_array_init(patterns, user->pool, 2);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi for(i=1;;i++) {
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct imap_match_glob **glob;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi const char *key = NOTIFY_STATUS_SETTING_MAILBOX_PREFIX;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if (i > 1)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi key = t_strdup_printf("%s%u", key, i);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi value = mail_user_plugin_getenv(user, key);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if (value == NULL)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi return;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi char sep = mail_namespace_get_sep(user->namespaces);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi glob = array_append_space(patterns);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi *glob = imap_match_init(user->pool, value, TRUE, sep);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi }
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi}
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic bool notify_status_mailbox_enabled(struct mailbox *box)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct mail_user *user = mail_storage_get_user(mailbox_get_storage(box));
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct notify_status_user *nuser = NOTIFY_STATUS_USER_CONTEXT(user);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct imap_match_glob **glob;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi /* not enabled */
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if (nuser == NULL)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi return FALSE;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi /* if no patterns defined, anything goes */
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if (array_count(&nuser->patterns) == 0)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi return TRUE;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi array_foreach_modifiable(&nuser->patterns, glob) {
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if ((imap_match(*glob, mailbox_get_vname(box)) & IMAP_MATCH_YES) != 0)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi return TRUE;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi }
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi return FALSE;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi}
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic void notify_update_callback(const struct dict_commit_result *result,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi void *context ATTR_UNUSED)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if (result->ret == DICT_COMMIT_RET_OK ||
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi result->ret == DICT_COMMIT_RET_NOTFOUND)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi return;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi i_error("notify-status: dict_transaction_commit failed: %s",
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi result->error == NULL ? "" : result->error);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi}
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
2e684ad58aa422c0950af4c609969a987dd1da67Aki Tuomi#define MAILBOX_STATUS_NOTIFY (STATUS_MESSAGES|STATUS_UNSEEN|\
2e684ad58aa422c0950af4c609969a987dd1da67Aki Tuomi STATUS_RECENT|STATUS_UIDNEXT|\
5c449ac9f4b353cb75b1a27f0fc96da3940829b8Timo Sirainen STATUS_UIDVALIDITY|\
2e684ad58aa422c0950af4c609969a987dd1da67Aki Tuomi STATUS_HIGHESTMODSEQ|STATUS_FIRST_RECENT_UID|\
2e684ad58aa422c0950af4c609969a987dd1da67Aki Tuomi STATUS_HIGHESTPVTMODSEQ)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic void notify_update_mailbox_status(struct mailbox *box)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct mail_user *user = mail_storage_get_user(mailbox_get_storage(box));
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct notify_status_user *nuser = NOTIFY_STATUS_USER_CONTEXT(user);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi i_assert(nuser != NULL);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct dict_transaction_context *t;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct mailbox_status status;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if (user->mail_debug)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi i_debug("notify-status: Updating mailbox %s status",
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi mailbox_get_vname(box));
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi box = mailbox_alloc(mailbox_get_namespace(box)->list,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi mailbox_get_vname(box), MAILBOX_FLAG_READONLY);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if (mailbox_open(box) < 0) {
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi i_error("notify-status: mailbox_open(%s) failed: %s",
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi mailbox_get_vname(box),
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi mailbox_get_last_error(box, NULL));
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi } else if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) {
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi i_error("notify-status: mailbox_sync(%s) failed: %s",
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi mailbox_get_vname(box),
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi mailbox_get_last_error(box, NULL));
2e684ad58aa422c0950af4c609969a987dd1da67Aki Tuomi } else if (mailbox_get_status(box, MAILBOX_STATUS_NOTIFY,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi &status) < 0) {
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi i_error("notify-status: mailbox_get_status(%s) failed: %s",
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi mailbox_get_vname(box),
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi mailbox_get_last_error(box, NULL));
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi } else {
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi string_t *username = t_str_new(strlen(user->username));
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi string_t *mboxname = t_str_new(64);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi json_append_escaped(username, user->username);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi json_append_escaped(mboxname, mailbox_get_vname(box));
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi const struct var_expand_table values[] = {
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi { '\0', str_c(username), "username" },
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi { '\0', str_c(mboxname), "mailbox" },
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi { '\0', dec2str(status.messages), "messages" },
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi { '\0', dec2str(status.unseen), "unseen" },
2e684ad58aa422c0950af4c609969a987dd1da67Aki Tuomi { '\0', dec2str(status.recent), "recent" },
2e684ad58aa422c0950af4c609969a987dd1da67Aki Tuomi { '\0', dec2str(status.uidvalidity), "uidvalidity" },
2e684ad58aa422c0950af4c609969a987dd1da67Aki Tuomi { '\0', dec2str(status.uidnext), "uidnext" },
2e684ad58aa422c0950af4c609969a987dd1da67Aki Tuomi { '\0', dec2str(status.first_recent_uid), "first_recent_uid" },
2e684ad58aa422c0950af4c609969a987dd1da67Aki Tuomi { '\0', dec2str(status.highest_modseq), "highest_modseq" },
2e684ad58aa422c0950af4c609969a987dd1da67Aki Tuomi { '\0', dec2str(status.highest_pvt_modseq), "highest_pvt_modseq" },
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi { '\0', NULL, NULL }
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi };
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi const char *error;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi const char *key =
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi t_strdup_printf(NOTIFY_STATUS_KEY, mailbox_get_vname(box));
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi string_t *dest = t_str_new(64);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if (var_expand(dest, nuser->value_template, values, &error)<0) {
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi i_error("notify-status: var_expand(%s) failed: %s",
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi nuser->value_template, error);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi } else {
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi t = dict_transaction_begin(nuser->dict);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi dict_set(t, key, str_c(dest));
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi dict_transaction_commit_async(&t, notify_update_callback, NULL) ;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi }
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi }
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi mailbox_free(&box);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi}
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic void notify_remove_mailbox_status(struct mailbox *box)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct mail_user *user = mail_storage_get_user(mailbox_get_storage(box));
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct notify_status_user *nuser = NOTIFY_STATUS_USER_CONTEXT(user);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi i_assert(nuser != NULL);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct dict_transaction_context *t;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if (user->mail_debug)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi i_debug("notify-status: Removing mailbox %s status",
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi mailbox_get_vname(box));
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi const char *key =
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi t_strdup_printf(NOTIFY_STATUS_KEY, mailbox_get_vname(box));
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi t = dict_transaction_begin(nuser->dict);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi dict_unset(t, key);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi dict_transaction_commit_async(&t, notify_update_callback, NULL) ;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi}
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic void *notify_status_mail_transaction_begin(struct mailbox_transaction_context *t)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct notify_status_mail_txn *txn = i_new(struct notify_status_mail_txn, 1);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi txn->box = mailbox_transaction_get_mailbox(t);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi return txn;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi}
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic void
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuominotify_status_mail_transaction_commit(void *t,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct mail_transaction_commit_changes *changes ATTR_UNUSED)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct notify_status_mail_txn *txn = (struct notify_status_mail_txn *)t;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if (txn->changed && notify_status_mailbox_enabled(txn->box))
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi notify_update_mailbox_status(txn->box);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi i_free(txn);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi}
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic void notify_status_mail_transaction_rollback(void *t)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi i_free(t);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi}
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic void notify_status_mailbox_create(struct mailbox *box)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if (notify_status_mailbox_enabled(box))
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi notify_update_mailbox_status(box);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi}
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic void notify_status_mailbox_delete_commit(void *txn ATTR_UNUSED,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct mailbox *box)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if (notify_status_mailbox_enabled(box))
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi notify_remove_mailbox_status(box);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi}
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic void notify_status_mailbox_rename(struct mailbox *src, struct mailbox *dest)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if (notify_status_mailbox_enabled(src))
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi notify_remove_mailbox_status(src);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if (notify_status_mailbox_enabled(dest))
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi notify_update_mailbox_status(dest);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi}
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic void notify_status_mail_save(void *t, struct mail *mail ATTR_UNUSED)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct notify_status_mail_txn *txn = (struct notify_status_mail_txn *)t;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi txn->changed = TRUE;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi}
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic void notify_status_mail_copy(void *t, struct mail *src ATTR_UNUSED,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct mail *dst ATTR_UNUSED)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct notify_status_mail_txn *txn = (struct notify_status_mail_txn *)t;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi txn->changed = TRUE;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi}
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic void notify_status_mail_expunge(void *t, struct mail *mail ATTR_UNUSED)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct notify_status_mail_txn *txn = (struct notify_status_mail_txn *)t;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi txn->changed = TRUE;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi}
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic void notify_status_mail_update_flags(void *t, struct mail *mail,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi enum mail_flags old_flags)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct notify_status_mail_txn *txn = (struct notify_status_mail_txn *)t;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if ((old_flags & MAIL_SEEN) != (mail_get_flags(mail) & MAIL_SEEN))
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi txn->changed = TRUE;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi}
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic const struct notify_vfuncs notify_vfuncs =
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi .mail_transaction_begin = notify_status_mail_transaction_begin,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi .mail_save = notify_status_mail_save,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi .mail_copy = notify_status_mail_copy,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi .mail_expunge = notify_status_mail_expunge,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi .mail_update_flags = notify_status_mail_update_flags,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi .mail_transaction_commit = notify_status_mail_transaction_commit,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi .mail_transaction_rollback = notify_status_mail_transaction_rollback,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi .mailbox_create = notify_status_mailbox_create,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi .mailbox_delete_commit = notify_status_mailbox_delete_commit,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi .mailbox_rename = notify_status_mailbox_rename,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi};
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic void notify_status_mail_user_deinit(struct mail_user *user)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct notify_status_user *nuser = NOTIFY_STATUS_USER_CONTEXT(user);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi i_assert(nuser != NULL);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi dict_wait(nuser->dict);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi dict_deinit(&nuser->dict);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi notify_unregister(nuser->context);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi nuser->module_ctx.super.deinit(user);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi}
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic void notify_status_mail_user_created(struct mail_user *user)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct mail_user_vfuncs *v = user->vlast;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct notify_status_user *nuser;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct dict *dict;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi const char *error;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi const char *template = mail_user_plugin_getenv(user, NOTIFY_STATUS_SETTING_VALUE_TEMPLATE);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi const char *uri = mail_user_plugin_getenv(user, NOTIFY_STATUS_SETTING_DICT_URI);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if (user->autocreated)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi return;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if (uri == NULL || *uri == '\0') {
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if (user->mail_debug)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi i_debug("notify-status: Disabled - Missing plugin/"
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi NOTIFY_STATUS_SETTING_DICT_URI" setting");
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi return;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi }
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if (template == NULL || *template == '\0')
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi template = NOTIFY_STATUS_SETTING_VALUE_TEMPLATE_DEFAULT;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if (notify_status_dict_init(user, uri, &dict, &error) < 0) {
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi i_error("notify-status: %s", error);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi return;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi }
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi nuser = p_new(user->pool, struct notify_status_user, 1);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi nuser->module_ctx.super = *v;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi nuser->dict = dict;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi user->vlast = &nuser->module_ctx.super;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi v->deinit = notify_status_mail_user_deinit;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi /* either static value or lifetime is user object's lifetime */
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi nuser->value_template = template;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi MODULE_CONTEXT_SET(user, notify_status_user_module, nuser);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi}
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic void
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuominotify_status_mail_namespaces_created(struct mail_namespace *namespaces)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct mail_user *user = namespaces->user;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi struct notify_status_user *nuser = NOTIFY_STATUS_USER_CONTEXT(user);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi if (nuser == NULL)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi return;
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi notify_status_mailbox_patterns_init(user, &nuser->patterns);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi nuser->context = notify_register(&notify_vfuncs);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi}
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomistatic const struct mail_storage_hooks notify_storage_hooks =
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi .mail_user_created = notify_status_mail_user_created,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi .mail_namespaces_created = notify_status_mail_namespaces_created,
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi};
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomivoid notify_status_plugin_init(struct module *module)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi mail_storage_hooks_add(module, &notify_storage_hooks);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi}
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomivoid notify_status_plugin_deinit(void)
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi{
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi mail_storage_hooks_remove(&notify_storage_hooks);
f97dcee675e94c4a8ce17506e2c52ff5f1e81cf5Aki Tuomi}