[dovecot-cvs] dovecot/src/plugins/quota .cvsignore, NONE, 1.1 Makefile.am, NONE, 1.1 quota-dict.c, NONE, 1.1 quota-dirsize.c, NONE, 1.1 quota-plugin.c, NONE, 1.1 quota-plugin.h, NONE, 1.1 quota-private.h, NONE, 1.1 quota-storage.c, NONE, 1.1 quota.c, NONE, 1.1 quota.h, NONE, 1.1

cras at dovecot.org cras at dovecot.org
Sat Dec 10 21:44:49 EET 2005


Update of /var/lib/cvs/dovecot/src/plugins/quota
In directory talvi:/tmp/cvs-serv32061/src/plugins/quota

Added Files:
	.cvsignore Makefile.am quota-dict.c quota-dirsize.c 
	quota-plugin.c quota-plugin.h quota-private.h quota-storage.c 
	quota.c quota.h 
Log Message:
Added beginnings of plugin infrastructure. TODO: These could be optionally
compiled into binaries with some configure options.

Added quota plugin and a new trash plugin. Not very well tested.



--- NEW FILE: .cvsignore ---
*.la
*.lo
*.o
.deps
.libs
Makefile
Makefile.in
so_locations

--- NEW FILE: Makefile.am ---
AM_CPPFLAGS = \
	-I$(top_srcdir)/src/lib \
	-I$(top_srcdir)/src/lib-dict \
	-I$(top_srcdir)/src/lib-mail \
	-I$(top_srcdir)/src/lib-storage

libquota_plugin_la_LDFLAGS = -module -avoid-version

module_LTLIBRARIES = \
	libquota_plugin.la

libquota_plugin_la_SOURCES = \
	quota.c \
	quota-dict.c \
	quota-dirsize.c \
        quota-plugin.c \
	quota-storage.c

install-exec-local:
	$(mkdir_p) $(moduledir)/imap $(moduledir)/lda
	for d in imap lda; do \
	  rm -f $(moduledir)/$$d/libquota_plugin.so; \
	  $(LN_S) ../libquota_plugin.so $(moduledir)/$$d; \
	done

--- NEW FILE: quota-dict.c ---
/* Copyright (C) 2005 Timo Sirainen */

#include "lib.h"
#include "str.h"
#include "dict.h"
#include "quota-private.h"

#include <stdlib.h>

#define DICT_QUOTA_LIMIT_PATH DICT_PATH_PRIVATE"quota/limit/"
#define DICT_QUOTA_CURRENT_PATH DICT_PATH_PRIVATE"quota/current/"

struct dict_quota {
	struct quota quota;

	pool_t pool;
	const char *error;
	struct quota_root root;

	struct dict *dict;
};

struct dict_quota_root_iter {
	struct quota_root_iter iter;

	int sent;
};

struct dict_quota_transaction_context {
	struct quota_transaction_context ctx;

	uint64_t storage_limit;
	uint64_t storage_current;
};

extern struct quota dict_quota;

static struct quota *dict_quota_init(const char *data)
{
	struct dict_quota *quota;
	struct dict *dict;
	pool_t pool;

	if (getenv("DEBUG") != NULL)
		i_info("dict quota uri = %s", data);

	dict = dict_init(data);
	if (dict == NULL)
		return NULL;

	pool = pool_alloconly_create("quota", 1024);
	quota = p_new(pool, struct dict_quota, 1);
	quota->pool = pool;
	quota->quota = dict_quota;
	quota->dict = dict;

	quota->root.quota = &quota->quota;
	return &quota->quota;
}

static void dict_quota_deinit(struct quota *_quota)
{
	struct dict_quota *quota = (struct dict_quota *)_quota;

	pool_unref(quota->pool);
}

static struct quota_root_iter *
dict_quota_root_iter_init(struct quota *quota,
			  struct mailbox *box __attr_unused__)
{
	struct dict_quota_root_iter *iter;

	iter = i_new(struct dict_quota_root_iter, 1);
	iter->iter.quota = quota;
	return &iter->iter;
}

static struct quota_root *
dict_quota_root_iter_next(struct quota_root_iter *_iter)
{
	struct dict_quota_root_iter *iter =
		(struct dict_quota_root_iter *)_iter;
	struct dict_quota *quota = (struct dict_quota *)_iter->quota;

	if (iter->sent)
		return NULL;

	iter->sent = TRUE;
	return &quota->root;
}

static int dict_quota_root_iter_deinit(struct quota_root_iter *iter)
{
	i_free(iter);
	return 0;
}

static struct quota_root *
dict_quota_root_lookup(struct quota *_quota, const char *name)
{
	struct dict_quota *quota = (struct dict_quota *)_quota;

	if (*name == '\0')
		return &quota->root;
	else
		return NULL;
}

static const char *
dict_quota_root_get_name(struct quota_root *root __attr_unused__)
{
	return "";
}

static const char *const *
dict_quota_root_get_resources(struct quota_root *root __attr_unused__)
{
	static const char *resources[] = { QUOTA_NAME_STORAGE, NULL };

	return resources;
}

static int
dict_quota_root_create(struct quota *_quota,
		       const char *name __attr_unused__,
		       struct quota_root **root_r __attr_unused__)
{
	struct dict_quota *quota = (struct dict_quota *)_quota;

        quota->error = "Permission denied";
	return -1;
}

static int
dict_quota_get_resource(struct quota_root *root, const char *name,
			uint64_t *value_r, uint64_t *limit_r)
{
	struct dict_quota *quota = (struct dict_quota *)root->quota;
	const char *value;

	if (quota->dict == NULL)
		return 0;

	t_push();
	value = dict_lookup(quota->dict, unsafe_data_stack_pool,
			    t_strconcat(DICT_QUOTA_LIMIT_PATH, name, NULL));
	*limit_r = value == NULL ? 0 : strtoull(value, NULL, 10);

	if (value == NULL) {
		/* resource doesn't exist */
		*value_r = 0;
	} else {
		value = dict_lookup(quota->dict, unsafe_data_stack_pool,
				    t_strconcat(DICT_QUOTA_CURRENT_PATH,
						name, NULL));
		*value_r = value == NULL ? 0 : strtoull(value, NULL, 10);
	}
	t_pop();

	*limit_r /= 1024;
	*value_r /= 1024;

	return value == NULL;
}

static int
dict_quota_set_resource(struct quota_root *root,
			const char *name __attr_unused__,
			uint64_t value __attr_unused__)
{
	struct dict_quota *quota = (struct dict_quota *)root->quota;

	quota->error = "Permission denied";
	return -1;
}

static struct quota_transaction_context *
dict_quota_transaction_begin(struct quota *_quota)
{
	struct dict_quota *quota = (struct dict_quota *)_quota;
	struct dict_quota_transaction_context *ctx;
	const char *value;

	ctx = i_new(struct dict_quota_transaction_context, 1);
	ctx->ctx.quota = _quota;

	if (quota->dict != NULL) {
		t_push();
		value = dict_lookup(quota->dict, unsafe_data_stack_pool,
				    DICT_QUOTA_LIMIT_PATH"storage");
		ctx->storage_limit = value == NULL ? 0 :
			strtoull(value, NULL, 10);

		value = dict_lookup(quota->dict, unsafe_data_stack_pool,
				    DICT_QUOTA_CURRENT_PATH"storage");
		ctx->storage_current = value == NULL ? 0 :
			strtoull(value, NULL, 10);
		t_pop();
	} else {
		ctx->storage_limit = (uint64_t)-1;
	}

	return &ctx->ctx;
}

static int
dict_quota_transaction_commit(struct quota_transaction_context *_ctx)
{
	struct dict_quota_transaction_context *ctx =
		(struct dict_quota_transaction_context *)_ctx;
	struct dict_quota *quota = (struct dict_quota *)_ctx->quota;

	if (quota->dict != NULL) {
		struct dict_transaction_context *dt;

		dt = dict_transaction_begin(quota->dict);
		dict_atomic_inc(dt, DICT_QUOTA_CURRENT_PATH"storage",
				_ctx->bytes_diff);
		if (dict_transaction_commit(dt) < 0)
			i_error("dict_quota: Couldn't update quota");
	}

	i_free(ctx);
	return 0;
}

static void
dict_quota_transaction_rollback(struct quota_transaction_context *ctx)
{
	i_free(ctx);
}

static int
dict_quota_try_alloc(struct quota_transaction_context *_ctx,
		     struct mail *mail, int *too_large_r)
{
	struct dict_quota_transaction_context *ctx =
		(struct dict_quota_transaction_context *)_ctx;
	uoff_t size;

	size = mail_get_physical_size(mail);
	*too_large_r = size > ctx->storage_limit;

	if (ctx->storage_current + _ctx->bytes_diff + size > ctx->storage_limit)
		return 0;

	_ctx->bytes_diff += size;
	return 1;
}

static void
dict_quota_alloc(struct quota_transaction_context *ctx, struct mail *mail)
{
	uoff_t size;

	size = mail_get_physical_size(mail);
	if (size != (uoff_t)-1)
		ctx->bytes_diff += size;
}

static void
dict_quota_free(struct quota_transaction_context *ctx, struct mail *mail)
{
	uoff_t size;

	size = mail_get_physical_size(mail);
	if (size != (uoff_t)-1)
		ctx->bytes_diff -= size;
}

static const char *dict_quota_last_error(struct quota *_quota)
{
	struct dict_quota *quota = (struct dict_quota *)_quota;

	return quota->error;
}

struct quota dict_quota = {
	"dict",

	dict_quota_init,
	dict_quota_deinit,

	dict_quota_root_iter_init,
	dict_quota_root_iter_next,
	dict_quota_root_iter_deinit,

	dict_quota_root_lookup,

	dict_quota_root_get_name,
	dict_quota_root_get_resources,

	dict_quota_root_create,
	dict_quota_get_resource,
	dict_quota_set_resource,

	dict_quota_transaction_begin,
	dict_quota_transaction_commit,
	dict_quota_transaction_rollback,

	dict_quota_try_alloc,
	dict_quota_alloc,
	dict_quota_free,

	dict_quota_last_error,

	ARRAY_INIT
};

--- NEW FILE: quota-dirsize.c ---
/* Copyright (C) 2005 Timo Sirainen */

/* Quota reporting based on simply summing sizes of all files in mailbox
   together. */

#include "lib.h"
#include "str.h"
#include "quota-private.h"

#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>

struct dirsize_quota {
	struct quota quota;

	pool_t pool;
	const char *path;
	const char *error;
	struct quota_root root;

	uint64_t storage_limit;
};

struct dirsize_quota_root_iter {
	struct quota_root_iter iter;

	int sent;
};

extern struct quota dirsize_quota;

static struct quota *dirsize_quota_init(const char *data)
{
	struct dirsize_quota *quota;
	const char *const *args;
	pool_t pool;

	pool = pool_alloconly_create("quota", 1024);
	quota = p_new(pool, struct dirsize_quota, 1);
	quota->pool = pool;
	quota->quota = dirsize_quota;

	args = t_strsplit(data, ":");
	quota->path = p_strdup(pool, args[0]);

	for (args++; *args != '\0'; args++) {
		if (strncmp(*args, "storage=", 8) == 0)
			quota->storage_limit = strtoull(*args + 8, NULL, 10);
	}

	if (getenv("DEBUG") != NULL) {
		i_info("dirsize quota path = %s", quota->path);
		i_info("dirsize quota limit = %llukB",
		       (unsigned long long)quota->storage_limit);
	}

	quota->root.quota = &quota->quota;
	return &quota->quota;
}

static void dirsize_quota_deinit(struct quota *_quota)
{
	struct dirsize_quota *quota = (struct dirsize_quota *)_quota;

	pool_unref(quota->pool);
}

static struct quota_root_iter *
dirsize_quota_root_iter_init(struct quota *quota,
			     struct mailbox *box __attr_unused__)
{
	struct dirsize_quota_root_iter *iter;

	iter = i_new(struct dirsize_quota_root_iter, 1);
	iter->iter.quota = quota;
	return &iter->iter;
}

static struct quota_root *
dirsize_quota_root_iter_next(struct quota_root_iter *_iter)
{
	struct dirsize_quota_root_iter *iter =
		(struct dirsize_quota_root_iter *)_iter;
	struct dirsize_quota *quota = (struct dirsize_quota *)_iter->quota;

	if (iter->sent)
		return NULL;

	iter->sent = TRUE;
	return &quota->root;
}

static int dirsize_quota_root_iter_deinit(struct quota_root_iter *iter)
{
	i_free(iter);
	return 0;
}

static struct quota_root *
dirsize_quota_root_lookup(struct quota *_quota, const char *name)
{
	struct dirsize_quota *quota = (struct dirsize_quota *)_quota;

	if (*name == '\0')
		return &quota->root;
	else
		return NULL;
}

static const char *
dirsize_quota_root_get_name(struct quota_root *root __attr_unused__)
{
	return "";
}

static const char *const *
dirsize_quota_root_get_resources(struct quota_root *root __attr_unused__)
{
	static const char *resources[] = { QUOTA_NAME_STORAGE, NULL };

	return resources;
}

static int
dirsize_quota_root_create(struct quota *_quota,
			  const char *name __attr_unused__,
			  struct quota_root **root_r __attr_unused__)
{
	struct dirsize_quota *quota = (struct dirsize_quota *)_quota;

        quota->error = "Permission denied";
	return -1;
}

static int get_dir_usage(const char *dir, uint64_t *value)
{
	DIR *dirp;
	string_t *path;
	struct dirent *d;
	struct stat st;
	unsigned int path_pos;
        int ret;

	dirp = opendir(dir);
	if (dirp == NULL) {
		if (errno == ENOENT)
			return 0;

		i_error("opendir(%s) failed: %m", dir);
		return -1;
	}

	path = t_str_new(128);
	str_append(path, dir);
	str_append_c(path, '/');
	path_pos = str_len(path);

	ret = 0;
	while ((d = readdir(dirp)) != NULL) {
		if (d->d_name[0] == '.' &&
		    (d->d_name[1] == '\0' ||
		     (d->d_name[1] == '.' && d->d_name[2] == '\0'))) {
			/* skip . and .. */
			continue;
		}

		str_truncate(path, path_pos);
		str_append(path, d->d_name);

		if (lstat(str_c(path), &st) < 0) {
			if (errno == ENOENT)
				continue;

			i_error("lstat(%s) failed: %m", dir);
			ret = -1;
			break;
		} else if (S_ISDIR(st.st_mode)) {
			if (get_dir_usage(str_c(path), value) < 0) {
				ret = -1;
				break;
			}
		} else {
			*value += st.st_size;
		}
	}

	(void)closedir(dirp);
	return ret;
}

static int
dirsize_quota_get_resource(struct quota_root *root, const char *name,
			   uint64_t *value_r, uint64_t *limit_r)
{
	struct dirsize_quota *quota = (struct dirsize_quota *)root->quota;

	*value_r = 0;
	*limit_r = 0;

	if (strcasecmp(name, QUOTA_NAME_STORAGE) != 0)
		return 0;

	if (get_dir_usage(quota->path, value_r) < 0) {
		quota->error = "Internal quota calculation error";
		return -1;
	}
	*value_r /= 1024;
	*limit_r = quota->storage_limit;
	return 1;
}

static int
dirsize_quota_set_resource(struct quota_root *root,
			   const char *name __attr_unused__,
			   uint64_t value __attr_unused__)
{
	struct dirsize_quota *quota = (struct dirsize_quota *)root->quota;

	quota->error = "Permission denied";
	return -1;
}

static struct quota_transaction_context *
dirsize_quota_transaction_begin(struct quota *quota)
{
	struct quota_transaction_context *ctx;

	ctx = i_new(struct quota_transaction_context, 1);
	ctx->quota = quota;
	return ctx;
}

static int
dirsize_quota_transaction_commit(struct quota_transaction_context *ctx)
{
	i_free(ctx);
	return 0;
}

static void
dirsize_quota_transaction_rollback(struct quota_transaction_context *ctx)
{
	i_free(ctx);
}

static int
dirsize_quota_try_alloc(struct quota_transaction_context *ctx,
			struct mail *mail, int *too_large_r)
{
	struct dirsize_quota *quota = (struct dirsize_quota *)ctx->quota;
	uint64_t value = 0;
	uoff_t size;

	size = mail_get_physical_size(mail);
	*too_large_r = size / 1024 > quota->storage_limit;

	if (get_dir_usage(quota->path, &value) < 0 || size == (uoff_t)-1) {
		quota->error = "Internal quota calculation error";
		return -1;
	}
	value += ctx->bytes_diff;

	if ((value + size) / 1024 > quota->storage_limit)
		return 0;

	ctx->bytes_diff += size;
	return 1;
}

static void
dirsize_quota_alloc(struct quota_transaction_context *ctx, struct mail *mail)
{
	uoff_t size;

	size = mail_get_physical_size(mail);
	if (size != (uoff_t)-1)
		ctx->bytes_diff += size;
}

static void
dirsize_quota_free(struct quota_transaction_context *ctx, struct mail *mail)
{
	uoff_t size;

	size = mail_get_physical_size(mail);
	if (size != (uoff_t)-1)
		ctx->bytes_diff -= size;
}

static const char *dirsize_quota_last_error(struct quota *_quota)
{
	struct dirsize_quota *quota = (struct dirsize_quota *)_quota;

	return quota->error;
}

struct quota dirsize_quota = {
	"dirsize",

	dirsize_quota_init,
	dirsize_quota_deinit,

	dirsize_quota_root_iter_init,
	dirsize_quota_root_iter_next,
	dirsize_quota_root_iter_deinit,

	dirsize_quota_root_lookup,

	dirsize_quota_root_get_name,
	dirsize_quota_root_get_resources,

	dirsize_quota_root_create,
	dirsize_quota_get_resource,
	dirsize_quota_set_resource,

	dirsize_quota_transaction_begin,
	dirsize_quota_transaction_commit,
	dirsize_quota_transaction_rollback,

	dirsize_quota_try_alloc,
	dirsize_quota_alloc,
	dirsize_quota_free,

	dirsize_quota_last_error,

	ARRAY_INIT
};

--- NEW FILE: quota-plugin.c ---
/* Copyright (C) 2005 Timo Sirainen */

#include "lib.h"
#include "str.h"
#include "mail-storage.h"
#include "quota.h"
#include "quota-plugin.h"

#include <stdlib.h>

/* defined by imap, pop3, lda */
extern void (*hook_mail_storage_created)(struct mail_storage *storage);

void (*quota_next_hook_mail_storage_created)(struct mail_storage *storage);
struct quota *quota = NULL;

void quota_plugin_init(void)
{
	const char *env;

	env = getenv("QUOTA");
	quota = env == NULL ? NULL : quota_init(env);

	if (quota != NULL) {
		quota_next_hook_mail_storage_created =
			hook_mail_storage_created;
		hook_mail_storage_created = quota_mail_storage_created;
	}
}

void quota_plugin_deinit(void)
{
	if (quota != NULL) {
		hook_mail_storage_created =
			quota_next_hook_mail_storage_created;
		quota_deinit(quota);
	}
}

--- NEW FILE: quota-plugin.h ---
#ifndef __QUOTA_PLUGIN_H
#define __QUOTA_PLUGIN_H

struct mail_storage;

extern void (*quota_next_hook_mail_storage_created)
	(struct mail_storage *storage);
extern struct quota *quota;

void quota_mail_storage_created(struct mail_storage *storage);

void quota_plugin_init(void);
void quota_plugin_deinit(void);

#endif

--- NEW FILE: quota-private.h ---
#ifndef __QUOTA_PRIVATE_H
#define __QUOTA_PRIVATE_H

#include "mail-storage-private.h"
#include "quota.h"

/* Modules should use do "my_id = quota_module_id++" and
   use quota_module_contexts[id] for their own purposes. */
extern unsigned int quota_module_id;

struct quota {
	const char *name;

	struct quota *(*init)(const char *data);
	void (*deinit)(struct quota *quota);

	struct quota_root_iter *
		(*root_iter_init)(struct quota *quota, struct mailbox *box);
	struct quota_root *(*root_iter_next)(struct quota_root_iter *iter);
	int (*root_iter_deinit)(struct quota_root_iter *iter);

	struct quota_root *(*root_lookup)(struct quota *quota,
					  const char *name);

	const char *(*root_get_name)(struct quota_root *root);
	const char *const *(*root_get_resources)(struct quota_root *root);

	int (*root_create)(struct quota *quota, const char *name,
			   struct quota_root **root_r);
	int (*get_resource)(struct quota_root *root, const char *name,
			    uint64_t *value_r, uint64_t *limit_r);
	int (*set_resource)(struct quota_root *root,
			    const char *name, uint64_t value);

	struct quota_transaction_context *
		(*transaction_begin)(struct quota *quota);
	int (*transaction_commit)(struct quota_transaction_context *ctx);
	void (*transaction_rollback)(struct quota_transaction_context *ctx);

	int (*try_alloc)(struct quota_transaction_context *ctx,
			 struct mail *mail, int *too_large_r);
	void (*alloc)(struct quota_transaction_context *ctx, struct mail *mail);
	void (*free)(struct quota_transaction_context *ctx, struct mail *mail);

	const char *(*last_error)(struct quota *quota);

	/* Module-specific contexts. See quota_module_id. */
	array_t ARRAY_DEFINE(quota_module_contexts, void);
};

struct quota_root {
	struct quota *quota;
};

struct quota_root_iter {
	struct quota *quota;
};

struct quota_transaction_context {
	struct quota *quota;

	int count_diff;
	int64_t bytes_diff;
};

#endif

--- NEW FILE: quota-storage.c ---
/* Copyright (C) 2005 Timo Sirainen */

#include "lib.h"
#include "array.h"
#include "istream.h"
#include "mail-storage-private.h"
#include "quota.h"
#include "quota-plugin.h"

#include <sys/stat.h>

#define QUOTA_CONTEXT(obj) \
	*((void **)array_idx_modifyable(&(obj)->module_contexts, \
					quota_storage_module_id))

struct quota_mail_storage {
	struct mail_storage_vfuncs super;
};

struct quota_mailbox {
	struct mailbox_vfuncs super;

	unsigned int save_hack:1;
};

struct quota_mail {
	struct mail_vfuncs super;
};

static unsigned int quota_storage_module_id = 0;
static int quota_storage_module_id_set = FALSE;

static int quota_mail_expunge(struct mail *_mail)
{
	struct mail_private *mail = (struct mail_private *)_mail;
	struct quota_mail *qmail = QUOTA_CONTEXT(mail);
	struct quota_transaction_context *qt =
		QUOTA_CONTEXT(_mail->transaction);

	if (qmail->super.expunge(_mail) < 0)
		return -1;

	quota_free(qt, _mail);
	return 0;
}

static struct mailbox_transaction_context *
quota_mailbox_transaction_begin(struct mailbox *box,
				enum mailbox_transaction_flags flags)
{
	struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
	struct mailbox_transaction_context *t;
	struct quota_transaction_context *qt;

	t = qbox->super.transaction_begin(box, flags);
	qt = quota_transaction_begin(quota);

	array_idx_set(&t->module_contexts, quota_storage_module_id, &qt);
	return t;
}

static int
quota_mailbox_transaction_commit(struct mailbox_transaction_context *ctx,
				 enum mailbox_sync_flags flags)
{
	struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->box);
	struct quota_transaction_context *qt = QUOTA_CONTEXT(ctx);

	if (qbox->super.transaction_commit(ctx, flags) < 0) {
		quota_transaction_rollback(qt);
		return -1;
	} else {
		(void)quota_transaction_commit(qt);
		return 0;
	}
}

static void
quota_mailbox_transaction_rollback(struct mailbox_transaction_context *ctx)
{
	struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->box);
	struct quota_transaction_context *qt = QUOTA_CONTEXT(ctx);

	qbox->super.transaction_rollback(ctx);
	quota_transaction_rollback(qt);
}

static struct mail *
quota_mail_alloc(struct mailbox_transaction_context *t,
		 enum mail_fetch_field wanted_fields,
		 struct mailbox_header_lookup_ctx *wanted_headers)
{
	struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box);
	struct quota_mail *qmail;
	struct mail *_mail;
	struct mail_private *mail;

	_mail = qbox->super.mail_alloc(t, wanted_fields, wanted_headers);
	mail = (struct mail_private *)_mail;

	qmail = p_new(mail->pool, struct quota_mail, 1);
	qmail->super = mail->v;

	mail->v.expunge = quota_mail_expunge;
	array_idx_set(&mail->module_contexts, quota_storage_module_id, &qmail);
	return _mail;
}

static int quota_check(struct mailbox_transaction_context *t, struct mail *mail)
{
	struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
	int ret, too_large;

	ret = quota_try_alloc(qt, mail, &too_large);
	if (ret > 0)
		return 0;
	else if (ret == 0) {
		mail_storage_set_error(t->box->storage, "Quota exceeded");
		return -1;
	} else {
		mail_storage_set_error(t->box->storage,  "%s",
				       quota_last_error(quota));
		return -1;
	}
}

static int
quota_copy(struct mailbox_transaction_context *t, struct mail *mail,
	   enum mail_flags flags, struct mail_keywords *keywords,
	   struct mail *dest_mail)
{
	struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box);
	struct mail *copy_dest_mail;
	int ret;

	if (dest_mail != NULL)
		copy_dest_mail = dest_mail;
	else
                copy_dest_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE, NULL);

	qbox->save_hack = FALSE;
	if (qbox->super.copy(t, mail, flags, keywords, copy_dest_mail) < 0)
		return -1;

	/* if copying used saving internally, we already checked the quota
	   and set qbox->save_hack = TRUE. */
	ret = qbox->save_hack ? 0 : quota_check(t, copy_dest_mail);

	if (copy_dest_mail != dest_mail)
		mail_free(copy_dest_mail);
	return ret;
}

static struct mail_save_context *
quota_save_init(struct mailbox_transaction_context *t,
		enum mail_flags flags, struct mail_keywords *keywords,
		time_t received_date, int timezone_offset,
		const char *from_envelope, struct istream *input,
		int want_mail __attr_unused__)
{
	struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box);
	const struct stat *st;

	st = i_stream_stat(input, TRUE);
	if (st != NULL && st->st_size != -1) {
		/* FIXME: input size is known, check for quota.
		   the API needs changing however to do this, we'd need to
		   return failure before "+ OK".. */
	}

	/* note that we set want_mail = TRUE in here. */
	return qbox->super.save_init(t, flags, keywords, received_date,
				     timezone_offset, from_envelope,
				     input, TRUE);
}

static int quota_save_finish(struct mail_save_context *ctx,
			     struct mail *dest_mail)
{
	struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->transaction->box);
	struct mail *save_dest_mail;
	int ret;

	if (dest_mail != NULL)
		save_dest_mail = dest_mail;
	else {
		save_dest_mail = mail_alloc(ctx->transaction,
					    MAIL_FETCH_PHYSICAL_SIZE, NULL);
	}

	if (qbox->super.save_finish(ctx, save_dest_mail) < 0)
		return -1;

	qbox->save_hack = TRUE;
	ret = quota_check(ctx->transaction, save_dest_mail);

	if (save_dest_mail != dest_mail)
		mail_free(save_dest_mail);
	return ret;
}

static struct mailbox *
quota_mailbox_open(struct mail_storage *storage, const char *name,
		   struct istream *input, enum mailbox_open_flags flags)
{
	struct quota_mail_storage *qstorage = QUOTA_CONTEXT(storage);
	struct mailbox *box;
	struct quota_mailbox *qbox;

	box = qstorage->super.mailbox_open(storage, name, input, flags);
	if (box == NULL)
		return NULL;

	qbox = p_new(box->pool, struct quota_mailbox, 1);
	qbox->super = box->v;

	box->v.transaction_begin = quota_mailbox_transaction_begin;
	box->v.transaction_commit = quota_mailbox_transaction_commit;
	box->v.transaction_rollback = quota_mailbox_transaction_rollback;
	box->v.mail_alloc = quota_mail_alloc;
	box->v.save_init = quota_save_init;
	box->v.save_finish = quota_save_finish;
	box->v.copy = quota_copy;
	array_idx_set(&box->module_contexts, quota_storage_module_id, &qbox);
	return box;
}

void quota_mail_storage_created(struct mail_storage *storage)
{
	struct quota_mail_storage *qstorage;

	if (quota_next_hook_mail_storage_created != NULL)
		quota_next_hook_mail_storage_created(storage);

	qstorage = p_new(storage->pool, struct quota_mail_storage, 1);
	qstorage->super = storage->v;
	storage->v.mailbox_open = quota_mailbox_open;

	if (!quota_storage_module_id_set) {
		quota_storage_module_id = mail_storage_module_id++;
		quota_storage_module_id_set = TRUE;
	}

	array_idx_set(&storage->module_contexts,
		      quota_storage_module_id, &qstorage);
}

--- NEW FILE: quota.c ---
/* Copyright (C) 2005 Timo Sirainen */

#include "lib.h"
#include "array.h"
#include "quota-private.h"

unsigned int quota_module_id = 0;

extern struct quota dirsize_quota;
extern struct quota dict_quota;

static struct quota *quota_classes[] = { &dirsize_quota, &dict_quota };
#define QUOTA_CLASS_COUNT (sizeof(quota_classes)/sizeof(quota_classes[0]))

struct quota *quota_init(const char *data)
{
	struct quota *quota;
	const char *name, *p;
	unsigned int i;

	t_push();
	p = strchr(data, ':');
	if (p == NULL) {
		name = data;
		data = "";
	} else {
		name = t_strdup_until(data, p);
		data = p+1;
	}
	for (i = 0; i < QUOTA_CLASS_COUNT; i++) {
		if (strcmp(quota_classes[i]->name, name) == 0)
			break;
	}
	t_pop();

	quota = i == QUOTA_CLASS_COUNT ? NULL :
		quota_classes[i]->init(data);
	if (quota != NULL) {
		array_create(&quota->quota_module_contexts,
			     default_pool, sizeof(void *), 5);
	}
	return quota;
}

void quota_deinit(struct quota *quota)
{
	array_t *module_contexts = &quota->quota_module_contexts;

	quota->deinit(quota);
	array_free(module_contexts);
}

struct quota_root_iter *
quota_root_iter_init(struct quota *quota, struct mailbox *box)
{
	return quota->root_iter_init(quota, box);
}

struct quota_root *quota_root_iter_next(struct quota_root_iter *iter)
{
	return iter->quota->root_iter_next(iter);
}

int quota_root_iter_deinit(struct quota_root_iter *iter)
{
	return iter->quota->root_iter_deinit(iter);
}

struct quota_root *quota_root_lookup(struct quota *quota, const char *name)
{
	return quota->root_lookup(quota, name);
}

const char *quota_root_get_name(struct quota_root *root)
{
	return root->quota->root_get_name(root);
}

const char *const *quota_root_get_resources(struct quota_root *root)
{
	return root->quota->root_get_resources(root);
}

int quota_root_create(struct quota *quota, const char *name,
		      struct quota_root **root_r)
{
	return quota->root_create(quota, name, root_r);
}

int quota_get_resource(struct quota_root *root, const char *name,
		       uint64_t *value_r, uint64_t *limit_r)
{
	return root->quota->get_resource(root, name, value_r, limit_r);
}

int quota_set_resource(struct quota_root *root,
		       const char *name, uint64_t value)
{
	return root->quota->set_resource(root, name, value);
}

struct quota_transaction_context *quota_transaction_begin(struct quota *quota)
{
	return quota->transaction_begin(quota);
}

int quota_transaction_commit(struct quota_transaction_context *ctx)
{
	return ctx->quota->transaction_commit(ctx);
}

void quota_transaction_rollback(struct quota_transaction_context *ctx)
{
	ctx->quota->transaction_rollback(ctx);
}

int quota_try_alloc(struct quota_transaction_context *ctx,
		    struct mail *mail, int *too_large_r)
{
	return ctx->quota->try_alloc(ctx, mail, too_large_r);
}

void quota_alloc(struct quota_transaction_context *ctx, struct mail *mail)
{
	ctx->quota->alloc(ctx, mail);
}

void quota_free(struct quota_transaction_context *ctx, struct mail *mail)
{
	ctx->quota->free(ctx, mail);
}

const char *quota_last_error(struct quota *quota)
{
	return quota->last_error(quota);
}

--- NEW FILE: quota.h ---
#ifndef __QUOTA_H
#define __QUOTA_H

struct mail;
struct mailbox;

/* Message storage size kilobytes. */
#define QUOTA_NAME_STORAGE "STORAGE"
/* Number of messages. */
#define QUOTA_NAME_MESSAGES "MESSAGES"

struct quota;
struct quota_root;
struct quota_root_iter;
struct quota_transaction_context;

struct quota *quota_init(const char *data);
void quota_deinit(struct quota *quota);

/* List all quota roots. Returned quota roots are freed by quota_deinit(). */
struct quota_root_iter *
quota_root_iter_init(struct quota *quota, struct mailbox *box);
struct quota_root *quota_root_iter_next(struct quota_root_iter *iter);
int quota_root_iter_deinit(struct quota_root_iter *iter);

/* Return quota root or NULL. */
struct quota_root *quota_root_lookup(struct quota *quota, const char *name);

/* Returns name of the quota root. */
const char *quota_root_get_name(struct quota_root *root);
/* Return a list of all resources set for the quota root. */
const char *const *quota_root_get_resources(struct quota_root *root);

/* Create a new quota root. Returns 0 if OK, -1 if error (eg. permission
   denied). */
int quota_root_create(struct quota *quota, const char *name,
		      struct quota_root **root_r);
/* Returns 1 if quota value was found, 0 if not, -1 if error. */
int quota_get_resource(struct quota_root *root,
		       const char *name, uint64_t *value_r, uint64_t *limit_r);
/* Returns 0 if OK, -1 if error (eg. permission denied, invalid name). */
int quota_set_resource(struct quota_root *root,
		       const char *name, uint64_t value);

/* Start a new quota transaction. */
struct quota_transaction_context *quota_transaction_begin(struct quota *quota);
/* Commit quota transaction. Returns 0 if ok, -1 if failed. */
int quota_transaction_commit(struct quota_transaction_context *ctx);
/* Rollback quota transaction changes. */
void quota_transaction_rollback(struct quota_transaction_context *ctx);

/* Allocate from quota if there's space. Returns 1 if updated, 0 if not,
   -1 if error. If mail size is larger than even maximum allowed quota,
   too_large_r is set to TRUE. */
int quota_try_alloc(struct quota_transaction_context *ctx,
		    struct mail *mail, int *too_large_r);
/* Update quota by allocating/freeing space used by mail. */
void quota_alloc(struct quota_transaction_context *ctx, struct mail *mail);
void quota_free(struct quota_transaction_context *ctx, struct mail *mail);

/* Returns the last error message. */
const char *quota_last_error(struct quota *quota);

#endif



More information about the dovecot-cvs mailing list