view src/director/user-directory.c @ 11321:5f350b5ff6d9 HEAD

Added initial implementation of a director process (for NFS users). There are still some unimplemented features and bugs. Also changing mail server list doesn't yet make sure that other directors won't assign the same user to a different server at the same time.
author Timo Sirainen <tss@iki.fi>
date Wed, 19 May 2010 09:56:49 +0200
parents
children d6d59ff58511
line wrap: on
line source

/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "md5.h"
#include "hash.h"
#include "llist.h"
#include "mail-host.h"
#include "user-directory.h"

#define MAX_CLOCK_DRIFT_SECS 2

struct user_directory_iter {
	struct user_directory *dir;
	struct user *pos;
};

struct user_directory {
	/* const char *username => struct user* */
	struct hash_table *hash;
	/* sorted by time */
	struct user *head, *tail;

	ARRAY_DEFINE(iters, struct user_directory_iter *);

	unsigned int timeout_secs;
};

static void user_move_iters(struct user_directory *dir, struct user *user)
{
	struct user_directory_iter *const *iterp;

	array_foreach(&dir->iters, iterp) {
		if ((*iterp)->pos == user)
			(*iterp)->pos = user->next;
	}
}

static void user_free(struct user_directory *dir, struct user *user)
{
	i_assert(user->host->user_count > 0);
	user->host->user_count--;

	user_move_iters(dir, user);

	hash_table_remove(dir->hash, POINTER_CAST(user->username_hash));
	DLLIST2_REMOVE(&dir->head, &dir->tail, user);
	i_free(user);
}

static void user_directory_drop_expired(struct user_directory *dir)
{
	while (dir->head != NULL &&
	       ioloop_time > dir->head->timestamp + dir->timeout_secs)
		user_free(dir, dir->head);
}

struct user *user_directory_lookup(struct user_directory *dir,
				   unsigned int username_hash)
{
	user_directory_drop_expired(dir);

	return hash_table_lookup(dir->hash, POINTER_CAST(username_hash));
}

struct user *
user_directory_add(struct user_directory *dir, unsigned int username_hash,
		   struct mail_host *host, time_t timestamp)
{
	struct user *user;

	user = i_new(struct user, 1);
	user->username_hash = username_hash;
	user->host = host;
	user->host->user_count++;
	user->timestamp = timestamp;
	DLLIST2_APPEND(&dir->head, &dir->tail, user);

	hash_table_insert(dir->hash, POINTER_CAST(user->username_hash), user);
	return user;
}

void user_directory_refresh(struct user_directory *dir, struct user *user)
{
	user_move_iters(dir, user);

	user->timestamp = ioloop_time;
	DLLIST2_REMOVE(&dir->head, &dir->tail, user);
	DLLIST2_APPEND(&dir->head, &dir->tail, user);
}

void user_directory_remove_host(struct user_directory *dir,
				struct mail_host *host)
{
	struct user *user, *next;

	for (user = dir->head; user != NULL; user = next) {
		next = user->next;

		if (user->host == host)
			user_free(dir, user);
	}
}

unsigned int user_directory_get_username_hash(const char *username)
{
	unsigned char md5[MD5_RESULTLEN];
	unsigned int i, hash = 0;

	md5_get_digest(username, strlen(username), md5);
	for (i = 0; i < sizeof(hash); i++)
		hash = (hash << CHAR_BIT) | md5[i];
	return hash;
}

bool user_directory_user_has_connections(struct user_directory *dir,
					 struct user *user)
{
	return user->timestamp +
		dir->timeout_secs - MAX_CLOCK_DRIFT_SECS >= ioloop_time;
}

struct user_directory *user_directory_init(unsigned int timeout_secs)
{
	struct user_directory *dir;

	dir = i_new(struct user_directory, 1);
	dir->timeout_secs = timeout_secs;
	dir->hash = hash_table_create(default_pool, default_pool,
				      0, NULL, NULL);
	i_array_init(&dir->iters, 8);
	return dir;
}

void user_directory_deinit(struct user_directory **_dir)
{
	struct user_directory *dir = *_dir;

	*_dir = NULL;

	i_assert(array_count(&dir->iters) == 0);

	while (dir->head != NULL)
		user_free(dir, dir->head);
	hash_table_destroy(&dir->hash);
	array_free(&dir->iters);
	i_free(dir);
}

struct user_directory_iter *
user_directory_iter_init(struct user_directory *dir)
{
	struct user_directory_iter *iter;

	iter = i_new(struct user_directory_iter, 1);
	iter->dir = dir;
	iter->pos = dir->head;
	array_append(&dir->iters, &iter, 1);
	return iter;
}

struct user *user_directory_iter_next(struct user_directory_iter *iter)
{
	struct user *user;

	user = iter->pos;
	if (user == NULL)
		return FALSE;

	iter->pos = user->next;
	return user;
}

void user_directory_iter_deinit(struct user_directory_iter **_iter)
{
	struct user_directory_iter *iter = *_iter;
	struct user_directory_iter *const *iters;
	unsigned int i, count;

	*_iter = NULL;

	iters = array_get(&iter->dir->iters, &count);
	for (i = 0; i < count; i++) {
		if (iters[i] == iter) {
			array_delete(&iter->dir->iters, i, 1);
			break;
		}
	}
	i_free(iter);
}