last-login-plugin.c revision bcb4e51a409d94ae670de96afb8483a4f7855294
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen#include "lib.h"
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen#include "ioloop.h"
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen#include "dict.h"
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen#include "mail-user.h"
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen#include "mail-namespace.h"
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen#include "mail-storage-private.h"
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen#include "mail-storage-hooks.h"
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen#include "last-login-plugin.h"
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen#define LAST_LOGIN_USER_CONTEXT(obj) \
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen MODULE_CONTEXT(obj, last_login_user_module)
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen#define LAST_LOGIN_DEFAULT_KEY_PREFIX "last-login/"
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainenstruct last_login_user {
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen union mail_user_module_context module_ctx;
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen struct dict *dict;
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen struct timeout *to;
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen};
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
8aa049fd90cd03a66ec43018554bf3ba06815a10Timo Sirainenconst char *last_login_plugin_version = DOVECOT_ABI_VERSION;
8aa049fd90cd03a66ec43018554bf3ba06815a10Timo Sirainen
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(last_login_user_module,
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen &mail_user_module_register);
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainenstatic void last_login_dict_deinit(struct mail_user *user)
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen{
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen struct last_login_user *luser = LAST_LOGIN_USER_CONTEXT(user);
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen if (luser->dict != NULL) {
c4478af52de63804efef2055580adf1dfc8679c6Timo Sirainen dict_wait(luser->dict);
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen dict_deinit(&luser->dict);
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen }
477082109e06b912d868a2c05674f44cb880875bTimo Sirainen /* remove timeout after dict_wait(), which may trigger
477082109e06b912d868a2c05674f44cb880875bTimo Sirainen last_login_dict_commit() */
0d1b8b6bec79746c5d89d57dd8c1688946bd9237Josef 'Jeff' Sipek timeout_remove(&luser->to);
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen}
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainenstatic void last_login_user_deinit(struct mail_user *user)
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen{
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen struct last_login_user *luser = LAST_LOGIN_USER_CONTEXT(user);
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen last_login_dict_deinit(user);
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen luser->module_ctx.super.deinit(user);
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen}
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainenstatic void
f9258697fe2573b087d09645dfade044dfb27052Aki Tuomilast_login_dict_commit(const struct dict_commit_result *result,
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen void *context)
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen{
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen struct mail_user *user = context;
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen struct last_login_user *luser = LAST_LOGIN_USER_CONTEXT(user);
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
f9258697fe2573b087d09645dfade044dfb27052Aki Tuomi switch(result->ret) {
f9258697fe2573b087d09645dfade044dfb27052Aki Tuomi case DICT_COMMIT_RET_OK:
f9258697fe2573b087d09645dfade044dfb27052Aki Tuomi case DICT_COMMIT_RET_NOTFOUND:
f9258697fe2573b087d09645dfade044dfb27052Aki Tuomi break;
f9258697fe2573b087d09645dfade044dfb27052Aki Tuomi case DICT_COMMIT_RET_FAILED:
f9258697fe2573b087d09645dfade044dfb27052Aki Tuomi i_error("last_login_dict: Failed to write value for user %s: %s",
f9258697fe2573b087d09645dfade044dfb27052Aki Tuomi user->username, result->error);
f9258697fe2573b087d09645dfade044dfb27052Aki Tuomi break;
f9258697fe2573b087d09645dfade044dfb27052Aki Tuomi case DICT_COMMIT_RET_WRITE_UNCERTAIN:
f9258697fe2573b087d09645dfade044dfb27052Aki Tuomi i_error("last_login_dict: Write was unconfirmed (timeout or disconnect) for user %s: %s",
f9258697fe2573b087d09645dfade044dfb27052Aki Tuomi user->username, result->error);
f9258697fe2573b087d09645dfade044dfb27052Aki Tuomi break;
f9258697fe2573b087d09645dfade044dfb27052Aki Tuomi };
f9258697fe2573b087d09645dfade044dfb27052Aki Tuomi
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen /* don't deinit the dict immediately here, lib-dict will just crash */
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen luser->to = timeout_add(0, last_login_dict_deinit, user);
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen}
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainenstatic void last_login_mail_user_created(struct mail_user *user)
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen{
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen struct mail_user_vfuncs *v = user->vlast;
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen struct last_login_user *luser;
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen struct dict *dict;
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainen struct dict_settings set;
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen struct dict_transaction_context *trans;
4209bd278ff41fe352e8b993d849a061152829c8Timo Sirainen const char *dict_value, *key_name, *precision, *error;
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen if (user->autocreated) {
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen /* we want to handle only logged in users,
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen not lda's raw user or accessed shared users */
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen return;
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen }
0c5ed44cf93523f5ae6d118501a79fff6dddf7d7Timo Sirainen if (user->session_restored) {
0c5ed44cf93523f5ae6d118501a79fff6dddf7d7Timo Sirainen /* This is IMAP unhibernation, not a real login. */
0c5ed44cf93523f5ae6d118501a79fff6dddf7d7Timo Sirainen return;
0c5ed44cf93523f5ae6d118501a79fff6dddf7d7Timo Sirainen }
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen dict_value = mail_user_plugin_getenv(user, "last_login_dict");
ce3a152df54ff6bcb08af5d489a873bd85fcecddTimo Sirainen if (dict_value == NULL || dict_value[0] == '\0')
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen return;
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(&set);
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainen set.username = user->username;
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainen set.base_dir = user->set->base_dir;
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainen if (mail_user_get_home(user, &set.home_dir) <= 0)
9346506a9f4dd9a6285fe8595588e73161849235Timo Sirainen set.home_dir = NULL;
20e04227229970d148801c507946666e2a9bd838Timo Sirainen if (dict_init(dict_value, &set, &dict, &error) < 0) {
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen i_error("last_login_dict: dict_init(%s) failed: %s",
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen dict_value, error);
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen return;
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen }
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen luser = p_new(user->pool, struct last_login_user, 1);
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen luser->module_ctx.super = *v;
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen user->vlast = &luser->module_ctx.super;
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen v->deinit = last_login_user_deinit;
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen luser->dict = dict;
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen MODULE_CONTEXT_SET(user, last_login_user_module, luser);
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen key_name = mail_user_plugin_getenv(user, "last_login_key");
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen if (key_name == NULL) {
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen key_name = t_strdup_printf(LAST_LOGIN_DEFAULT_KEY_PREFIX"%s",
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen user->username);
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen }
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen key_name = t_strconcat(DICT_PATH_SHARED, key_name, NULL);
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
4209bd278ff41fe352e8b993d849a061152829c8Timo Sirainen precision = mail_user_plugin_getenv(user, "last_login_precision");
4209bd278ff41fe352e8b993d849a061152829c8Timo Sirainen
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen trans = dict_transaction_begin(dict);
4209bd278ff41fe352e8b993d849a061152829c8Timo Sirainen if (precision == NULL || strcmp(precision, "s") == 0)
4209bd278ff41fe352e8b993d849a061152829c8Timo Sirainen dict_set(trans, key_name, dec2str(ioloop_time));
4209bd278ff41fe352e8b993d849a061152829c8Timo Sirainen else if (strcmp(precision, "ms") == 0) {
4209bd278ff41fe352e8b993d849a061152829c8Timo Sirainen dict_set(trans, key_name, t_strdup_printf(
4209bd278ff41fe352e8b993d849a061152829c8Timo Sirainen "%ld%03u", (long)ioloop_timeval.tv_sec,
4209bd278ff41fe352e8b993d849a061152829c8Timo Sirainen (unsigned int)(ioloop_timeval.tv_usec/1000)));
4209bd278ff41fe352e8b993d849a061152829c8Timo Sirainen } else if (strcmp(precision, "us") == 0) {
4209bd278ff41fe352e8b993d849a061152829c8Timo Sirainen dict_set(trans, key_name, t_strdup_printf(
4209bd278ff41fe352e8b993d849a061152829c8Timo Sirainen "%ld%06u", (long)ioloop_timeval.tv_sec,
4209bd278ff41fe352e8b993d849a061152829c8Timo Sirainen (unsigned int)ioloop_timeval.tv_usec));
4209bd278ff41fe352e8b993d849a061152829c8Timo Sirainen } else if (strcmp(precision, "ns") == 0) {
4209bd278ff41fe352e8b993d849a061152829c8Timo Sirainen dict_set(trans, key_name, t_strdup_printf(
4209bd278ff41fe352e8b993d849a061152829c8Timo Sirainen "%ld%06u000", (long)ioloop_timeval.tv_sec,
4209bd278ff41fe352e8b993d849a061152829c8Timo Sirainen (unsigned int)ioloop_timeval.tv_usec));
4209bd278ff41fe352e8b993d849a061152829c8Timo Sirainen } else {
4209bd278ff41fe352e8b993d849a061152829c8Timo Sirainen i_error("last_login_dict: Invalid last_login_precision '%s'", precision);
4209bd278ff41fe352e8b993d849a061152829c8Timo Sirainen }
8f7e81b327b8b5bf34262f6755df6d4481760d23Timo Sirainen dict_transaction_no_slowness_warning(trans);
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen dict_transaction_commit_async(&trans, last_login_dict_commit, user);
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen}
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainenstatic struct mail_storage_hooks last_login_mail_storage_hooks = {
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen .mail_user_created = last_login_mail_user_created
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen};
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainenvoid last_login_plugin_init(struct module *module)
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen{
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen mail_storage_hooks_add(module, &last_login_mail_storage_hooks);
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen}
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainenvoid last_login_plugin_deinit(void)
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen{
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen mail_storage_hooks_remove(&last_login_mail_storage_hooks);
3008b30900f4de3e76f1a6d289444eb694f3d17cTimo Sirainen}