last-login-plugin.c revision ce3a152df54ff6bcb08af5d489a873bd85fcecdd
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (c) 2014-2016 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen#include "ioloop.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "dict.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "mail-user.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "mail-namespace.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "mail-storage-private.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "mail-storage-hooks.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "last-login-plugin.h"
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define LAST_LOGIN_USER_CONTEXT(obj) \
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen MODULE_CONTEXT(obj, last_login_user_module)
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen#define LAST_LOGIN_DEFAULT_KEY_PREFIX "last-login/"
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainenstruct last_login_user {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen union mail_user_module_context module_ctx;
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen struct dict *dict;
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen struct timeout *to;
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen};
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainenconst char *last_login_plugin_version = DOVECOT_ABI_VERSION;
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(last_login_user_module,
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen &mail_user_module_register);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void last_login_dict_deinit(struct mail_user *user)
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct last_login_user *luser = LAST_LOGIN_USER_CONTEXT(user);
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen if (luser->dict != NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen dict_wait(luser->dict);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen dict_deinit(&luser->dict);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* remove timeout after dict_wait(), which may trigger
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen last_login_dict_commit() */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (luser->to != NULL)
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen timeout_remove(&luser->to);
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void last_login_user_deinit(struct mail_user *user)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen struct last_login_user *luser = LAST_LOGIN_USER_CONTEXT(user);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen last_login_dict_deinit(user);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen luser->module_ctx.super.deinit(user);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenlast_login_dict_commit(const struct dict_commit_result *result ATTR_UNUSED,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen void *context)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_user *user = context;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct last_login_user *luser = LAST_LOGIN_USER_CONTEXT(user);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* don't deinit the dict immediately here, lib-dict will just crash */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen luser->to = timeout_add(0, last_login_dict_deinit, user);
53238473bf77147660aa6db9daa68a8a685e9381Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
53238473bf77147660aa6db9daa68a8a685e9381Timo Sirainenstatic void last_login_mail_user_created(struct mail_user *user)
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen{
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen struct mail_user_vfuncs *v = user->vlast;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct last_login_user *luser;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen struct dict *dict;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen struct dict_settings set;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen struct dict_transaction_context *trans;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen const char *dict_value, *key_name, *precision, *error;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen if (user->autocreated) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen /* we want to handle only logged in users,
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen not lda's raw user or accessed shared users */
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen return;
53238473bf77147660aa6db9daa68a8a685e9381Timo Sirainen }
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen dict_value = mail_user_plugin_getenv(user, "last_login_dict");
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen if (dict_value == NULL || dict_value[0] == '\0')
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen return;
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen memset(&set, 0, sizeof(set));
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen set.username = user->username;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen set.base_dir = user->set->base_dir;
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen if (mail_user_get_home(user, &set.home_dir) <= 0)
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen set.home_dir = NULL;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen if (dict_init(dict_value, &set, &dict, &error) < 0) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen i_error("last_login_dict: dict_init(%s) failed: %s",
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen dict_value, error);
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen return;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen }
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen luser = p_new(user->pool, struct last_login_user, 1);
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen luser->module_ctx.super = *v;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen user->vlast = &luser->module_ctx.super;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen v->deinit = last_login_user_deinit;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen luser->dict = dict;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen MODULE_CONTEXT_SET(user, last_login_user_module, luser);
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen key_name = mail_user_plugin_getenv(user, "last_login_key");
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen if (key_name == NULL) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen key_name = t_strdup_printf(LAST_LOGIN_DEFAULT_KEY_PREFIX"%s",
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen user->username);
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen }
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen key_name = t_strconcat(DICT_PATH_SHARED, key_name, NULL);
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen precision = mail_user_plugin_getenv(user, "last_login_precision");
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen trans = dict_transaction_begin(dict);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen if (precision == NULL || strcmp(precision, "s") == 0)
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen dict_set(trans, key_name, dec2str(ioloop_time));
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen else if (strcmp(precision, "ms") == 0) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen dict_set(trans, key_name, t_strdup_printf(
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen "%ld%03u", (long)ioloop_timeval.tv_sec,
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen (unsigned int)(ioloop_timeval.tv_usec/1000)));
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen } else if (strcmp(precision, "us") == 0) {
ace3c14e47a5a865df8aeea2fabc993b609dd163Timo Sirainen dict_set(trans, key_name, t_strdup_printf(
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen "%ld%06u", (long)ioloop_timeval.tv_sec,
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen (unsigned int)ioloop_timeval.tv_usec));
ace3c14e47a5a865df8aeea2fabc993b609dd163Timo Sirainen } else if (strcmp(precision, "ns") == 0) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen dict_set(trans, key_name, t_strdup_printf(
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen "%ld%06u000", (long)ioloop_timeval.tv_sec,
ace3c14e47a5a865df8aeea2fabc993b609dd163Timo Sirainen (unsigned int)ioloop_timeval.tv_usec));
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen } else {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen i_error("last_login_dict: Invalid last_login_precision '%s'", precision);
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen }
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen dict_transaction_commit_async(&trans, last_login_dict_commit, user);
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen}
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainenstatic struct mail_storage_hooks last_login_mail_storage_hooks = {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen .mail_user_created = last_login_mail_user_created
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen};
ace3c14e47a5a865df8aeea2fabc993b609dd163Timo Sirainen
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainenvoid last_login_plugin_init(struct module *module)
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen{
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen mail_storage_hooks_add(module, &last_login_mail_storage_hooks);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid last_login_plugin_deinit(void)
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen{
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen mail_storage_hooks_remove(&last_login_mail_storage_hooks);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen