[dovecot-cvs] dovecot/src/plugins/quota quota-dict.c, 1.13, 1.14 quota-dirsize.c, 1.12, 1.13 quota-fs.c, 1.18, 1.19 quota-maildir.c, 1.12, 1.13 quota-plugin.c, 1.3, 1.4 quota-private.h, 1.10, 1.11 quota-storage.c, 1.10, 1.11 quota.c, 1.11, 1.12 quota.h, 1.5, 1.6

tss-movial at dovecot.org tss-movial at dovecot.org
Sun Jul 30 20:58:46 EEST 2006


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

Modified Files:
	quota-dict.c quota-dirsize.c quota-fs.c quota-maildir.c 
	quota-plugin.c quota-private.h quota-storage.c quota.c quota.h 
Log Message:
Added support for multiple quota roots, rules and storages. The configuration
has changed, userdb now needs to return "quota_rule" settings instead. Much
of the code changes were done by Tianyan Liu.



Index: quota-dict.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-dict.c,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -d -r1.13 -r1.14
--- quota-dict.c	17 Jun 2006 12:21:21 -0000	1.13
+++ quota-dict.c	30 Jul 2006 17:58:43 -0000	1.14
@@ -14,113 +14,76 @@
 struct dict_quota_root {
 	struct quota_root root;
 	struct dict *dict;
-
-	uint64_t message_bytes_limit;
-	uint64_t message_count_limit;
 };
 
 extern struct quota_backend quota_backend_dict;
 
-static struct quota_root *
-dict_quota_init(struct quota_setup *setup, const char *name)
+static struct quota_root *dict_quota_alloc(void)
 {
 	struct dict_quota_root *root;
-	struct dict *dict;
-	const char *uri, *const *args;
-	unsigned long long message_bytes_limit = 0, message_count_limit = 0;
 
-	uri = strchr(setup->data, ' ');
-	if (uri == NULL) {
-		i_error("dict quota: URI missing from parameters: %s",
-			setup->data);
-		return NULL;
-	}
+	root = i_new(struct dict_quota_root, 1);
+	return &root->root;
+}
 
-	t_push();
-	args = t_strsplit(t_strdup_until(setup->data, uri++), ":");
-	for (; *args != '\0'; args++) {
-		if (strncmp(*args, "storage=", 8) == 0) {
-			message_bytes_limit =
-				strtoull(*args + 8, NULL, 10) * 1024;
-		} else if (strncmp(*args, "messages=", 9) == 0)
-			message_bytes_limit = strtoull(*args + 9, NULL, 10);
-	}
-	t_pop();
+static int dict_quota_init(struct quota_root *_root, const char *args)
+{
+	struct dict_quota_root *root = (struct dict_quota_root *)_root;
+	const char *username, *p;
 
-	if (getenv("DEBUG") != NULL) {
-		i_info("dict quota: uri = %s", uri);
-		i_info("dict quota: byte limit = %llu", message_bytes_limit);
-		i_info("dict quota: count limit = %llu", message_count_limit);
+	p = args == NULL ? NULL : strchr(args, ':');
+	if (p == NULL) {
+		i_error("dict quota: URI missing from parameters");
+		return -1;
 	}
 
-	dict = dict_init(uri, getenv("USER"));
-	if (dict == NULL)
-		return NULL;
+	username = t_strdup_until(args, p);
+	args = p+1;
 
-	root = i_new(struct dict_quota_root, 1);
-	root->root.name = i_strdup(name);
-	root->root.v = quota_backend_dict.v;
-	root->dict = dict;
+	if (*username == '\0')
+		username = getenv("USER");
 
-	root->message_bytes_limit =
-		message_bytes_limit == 0 ? (uint64_t)-1 : message_bytes_limit;
-	root->message_count_limit =
-		message_count_limit == 0 ? (uint64_t)-1 : message_count_limit;
-	return &root->root;
+	if (getenv("DEBUG") != NULL)
+		i_info("dict quota: uri = %s", args);
+
+	root->dict = dict_init(args, username);
+	return root->dict != NULL ? 0 : -1;
 }
 
 static void dict_quota_deinit(struct quota_root *_root)
 {
 	struct dict_quota_root *root = (struct dict_quota_root *)_root;
 
-	i_free(root->root.name);
+	if (root->dict != NULL)
+		dict_deinit(&root->dict);
 	i_free(root);
 }
 
-static bool
-dict_quota_add_storage(struct quota_root *root __attr_unused__,
-		       struct mail_storage *storage __attr_unused__)
-{
-	return TRUE;
-}
-
-static void
-dict_quota_remove_storage(struct quota_root *root __attr_unused__,
-			  struct mail_storage *storage __attr_unused__)
-{
-}
-
 static const char *const *
 dict_quota_root_get_resources(struct quota_root *root __attr_unused__)
 {
-	static const char *resources[] = { QUOTA_NAME_STORAGE, NULL };
+	static const char *resources[] = {
+		QUOTA_NAME_STORAGE, QUOTA_NAME_MESSAGES, NULL
+	};
 
 	return resources;
 }
 
 static int
 dict_quota_get_resource(struct quota_root *_root, const char *name,
-			uint64_t *value_r, uint64_t *limit_r)
+			uint64_t *value_r, uint64_t *limit __attr_unused__)
 {
 	struct dict_quota_root *root = (struct dict_quota_root *)_root;
 	const char *value;
 	int ret;
 
 	if (strcmp(name, QUOTA_NAME_STORAGE) == 0) {
-		if (root->message_bytes_limit == (uint64_t)-1)
-			return 0;
-
-		*limit_r = root->message_bytes_limit / 1024;
 		t_push();
 		ret = dict_lookup(root->dict, unsafe_data_stack_pool,
 				  DICT_QUOTA_CURRENT_BYTES_PATH, &value);
 		*value_r = ret <= 0 ? 0 : strtoull(value, NULL, 10) / 1024;
 		t_pop();
 	} else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) {
-		if (root->message_count_limit == (uint64_t)-1)
-			return 0;
-
-		*limit_r = root->message_count_limit;
 		t_push();
 		ret = dict_lookup(root->dict, unsafe_data_stack_pool,
 				  DICT_QUOTA_CURRENT_COUNT_PATH, &value);
@@ -130,101 +93,37 @@
 		return 0;
 	}
 
-	return 1;
-}
-
-static int
-dict_quota_set_resource(struct quota_root *root,
-			const char *name __attr_unused__,
-			uint64_t value __attr_unused__)
-{
-	quota_set_error(root->setup->quota, MAIL_STORAGE_ERR_NO_PERMISSION);
-	return -1;
-}
-
-static struct quota_root_transaction_context *
-dict_quota_transaction_begin(struct quota_root *_root,
-			     struct quota_transaction_context *_ctx)
-{
-	struct dict_quota_root *root = (struct dict_quota_root *)_root;
-	struct quota_root_transaction_context *ctx;
-	const char *value;
-
-	ctx = i_new(struct quota_root_transaction_context, 1);
-	ctx->root = _root;
-	ctx->ctx = _ctx;
-
-	ctx->bytes_limit = root->message_bytes_limit;
-	ctx->count_limit = root->message_count_limit;
-
-	t_push();
-	if (ctx->bytes_limit != (uint64_t)-1) {
-		(void)dict_lookup(root->dict, unsafe_data_stack_pool,
-				  DICT_QUOTA_CURRENT_BYTES_PATH, &value);
-		ctx->bytes_current = value == NULL ? 0 :
-			strtoull(value, NULL, 10);
-	}
-	if (ctx->count_limit != (uint64_t)-1) {
-		(void)dict_lookup(root->dict, unsafe_data_stack_pool,
-				  DICT_QUOTA_CURRENT_COUNT_PATH, &value);
-		ctx->count_current = value == NULL ? 0 :
-			strtoull(value, NULL, 10);
-	}
-	t_pop();
-	return ctx;
+	return ret;
 }
 
 static int
-dict_quota_transaction_commit(struct quota_root_transaction_context *ctx)
+dict_quota_update(struct quota_root *_root, 
+		  struct quota_transaction_context *ctx)
 {
-	struct dict_quota_root *root = (struct dict_quota_root *)ctx->root;
+	struct dict_quota_root *root = (struct dict_quota_root *) _root;
 	struct dict_transaction_context *dt;
 
 	dt = dict_transaction_begin(root->dict);
-	if (ctx->bytes_limit != (uint64_t)-1) {
-		dict_atomic_inc(dt, DICT_QUOTA_CURRENT_BYTES_PATH,
-				ctx->bytes_diff);
-	}
-	if (ctx->count_limit != (uint64_t)-1) {
-		dict_atomic_inc(dt, DICT_QUOTA_CURRENT_COUNT_PATH,
-				ctx->count_diff);
-	}
+	dict_atomic_inc(dt, DICT_QUOTA_CURRENT_BYTES_PATH,
+			ctx->bytes_used);
+	dict_atomic_inc(dt, DICT_QUOTA_CURRENT_COUNT_PATH,
+			ctx->count_used);
+	
 	if (dict_transaction_commit(dt) < 0)
-		i_error("dict_quota: Couldn't update quota");
-
-	i_free(ctx);
+		return -1;
 	return 0;
 }
 
-static void
-dict_quota_transaction_rollback(struct quota_root_transaction_context *ctx)
-{
-	i_free(ctx);
-}
-
 struct quota_backend quota_backend_dict = {
 	"dict",
 
 	{
+		dict_quota_alloc,
 		dict_quota_init,
 		dict_quota_deinit,
-
-		dict_quota_add_storage,
-		dict_quota_remove_storage,
-
+		NULL,
 		dict_quota_root_get_resources,
-
 		dict_quota_get_resource,
-		dict_quota_set_resource,
-
-		dict_quota_transaction_begin,
-		dict_quota_transaction_commit,
-		dict_quota_transaction_rollback,
-
-		quota_default_try_alloc,
-		quota_default_try_alloc_bytes,
-		quota_default_test_alloc_bytes,
-		quota_default_alloc,
-		quota_default_free
+		dict_quota_update
 	}
 };

Index: quota-dirsize.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-dirsize.c,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -d -r1.12 -r1.13
--- quota-dirsize.c	28 Jun 2006 13:11:00 -0000	1.12
+++ quota-dirsize.c	30 Jul 2006 17:58:43 -0000	1.13
@@ -13,60 +13,22 @@
 #include <dirent.h>
 #include <sys/stat.h>
 
-struct dirsize_quota_root {
-	struct quota_root root;
-
-	uint64_t storage_limit;
+struct quota_count_path {
+	const char *path;
+	bool is_file;
 };
+ARRAY_DEFINE_TYPE(quota_count_path, struct quota_count_path);
 
 extern struct quota_backend quota_backend_dirsize;
 
-static struct quota_root *
-dirsize_quota_init(struct quota_setup *setup, const char *name)
+static struct quota_root *dirsize_quota_alloc(void)
 {
-	struct dirsize_quota_root *root;
-	const char *const *args;
-
-	root = i_new(struct dirsize_quota_root, 1);
-	root->root.name = i_strdup(name);
-	root->root.v = quota_backend_dirsize.v;
-
-	t_push();
-	args = t_strsplit(setup->data, ":");
-
-	for (; *args != '\0'; args++) {
-		if (strncmp(*args, "storage=", 8) == 0)
-			root->storage_limit = strtoull(*args + 8, NULL, 10);
-	}
-	t_pop();
-
-	if (getenv("DEBUG") != NULL) {
-		i_info("dirsize quota limit = %llukB",
-		       (unsigned long long)root->storage_limit);
-	}
-
-	return &root->root;
+	return i_new(struct quota_root, 1);
 }
 
 static void dirsize_quota_deinit(struct quota_root *_root)
 {
-	struct dirsize_quota_root *root = (struct dirsize_quota_root *)_root;
-
-	i_free(root->root.name);
-	i_free(root);
-}
-
-static bool
-dirsize_quota_add_storage(struct quota_root *root __attr_unused__,
-			  struct mail_storage *storage __attr_unused__)
-{
-	return TRUE;
-}
-
-static void
-dirsize_quota_remove_storage(struct quota_root *root __attr_unused__,
-			     struct mail_storage *storage __attr_unused__)
-{
+	i_free(_root);
 }
 
 static const char *const *
@@ -133,8 +95,7 @@
 	return ret;
 }
 
-static int get_usage(struct dirsize_quota_root *root,
-		     const char *path, bool is_file, uint64_t *value_r)
+static int get_usage(const char *path, bool is_file, uint64_t *value_r)
 {
 	struct stat st;
 
@@ -148,21 +109,12 @@
 		}
 		*value_r += st.st_size;
 	} else {
-		if (get_dir_usage(path, value_r) < 0) {
-			quota_set_error(root->root.setup->quota,
-					"Internal quota calculation error");
+		if (get_dir_usage(path, value_r) < 0)
 			return -1;
-		}
 	}
 	return 0;
 }
 
-struct quota_count_path {
-	const char *path;
-	bool is_file;
-};
-ARRAY_DEFINE_TYPE(quota_count_path, struct quota_count_path);
-
 static void quota_count_path_add(ARRAY_TYPE(quota_count_path) *paths,
 				 const char *path, bool is_file)
 {
@@ -173,7 +125,7 @@
 	for (i = 0; i < count; i++) {
 		if (strncmp(count_path[i].path, path,
 			    strlen(count_path[i].path)) == 0) {
-			/* this path is already being counted */
+			/* this path has already been counted */
 			return;
 		}
 		if (strncmp(count_path[i].path, path, strlen(path)) == 0) {
@@ -191,7 +143,7 @@
 }
 
 static int
-get_quota_root_usage(struct dirsize_quota_root *root, uint64_t *value_r)
+get_quota_root_usage(struct quota_root *root, uint64_t *value_r)
 {
 	struct mail_storage *const *storages;
 	ARRAY_TYPE(quota_count_path) paths;
@@ -203,7 +155,7 @@
 	t_push();
 	ARRAY_CREATE(&paths, pool_datastack_create(),
 		     struct quota_count_path, 8);
-	storages = array_get(&root->root.storages, &count);
+	storages = array_get(&root->quota->storages, &count);
 	for (i = 0; i < count; i++) {
 		path = mail_storage_get_mailbox_path(storages[i], "", &is_file);
 		quota_count_path_add(&paths, path, is_file);
@@ -217,7 +169,7 @@
 	/* now sum up the found paths */
 	count_paths = array_get(&paths, &count);
 	for (i = 0; i < count; i++) {
-		if (get_usage(root, count_paths[i].path, count_paths[i].is_file,
+		if (get_usage(count_paths[i].path, count_paths[i].is_file,
 			      value_r) < 0) {
 			t_pop();
 			return -1;
@@ -225,97 +177,40 @@
 	}
 
 	t_pop();
-
 	return 0;
 }
 
 static int
 dirsize_quota_get_resource(struct quota_root *_root, const char *name,
-			   uint64_t *value_r, uint64_t *limit_r)
+			   uint64_t *value_r, uint64_t *limit __attr_unused__)
 {
-	struct dirsize_quota_root *root = (struct dirsize_quota_root *)_root;
-
-	*value_r = 0;
-	*limit_r = 0;
-
 	if (strcasecmp(name, QUOTA_NAME_STORAGE) != 0)
 		return 0;
 
-	if (get_quota_root_usage(root, value_r) < 0)
+	if (get_quota_root_usage(_root, value_r) < 0)
 		return -1;
 
 	*value_r /= 1024;
-	*limit_r = root->storage_limit;
 	return 1;
 }
 
-static int
-dirsize_quota_set_resource(struct quota_root *root,
-			   const char *name __attr_unused__,
-			   uint64_t value __attr_unused__)
-{
-	quota_set_error(root->setup->quota, MAIL_STORAGE_ERR_NO_PERMISSION);
-	return -1;
-}
-
-static struct quota_root_transaction_context *
-dirsize_quota_transaction_begin(struct quota_root *_root,
-				struct quota_transaction_context *_ctx)
-{
-	struct dirsize_quota_root *root = (struct dirsize_quota_root *)_root;
-	struct quota_root_transaction_context *ctx;
-
-	ctx = i_new(struct quota_root_transaction_context, 1);
-	ctx->root = _root;
-	ctx->ctx = _ctx;
-
-	/* Get dir usage only once at the beginning of transaction.
-	   When copying/appending lots of mails we don't want to re-read the
-	   entire directory structure after each mail. */
-	if (get_quota_root_usage(root, &ctx->bytes_current) < 0 ||
-	    ctx->bytes_current == (uint64_t)-1) {
-                ctx->bytes_current = (uint64_t)-1;
-		quota_set_error(_root->setup->quota,
-				"Internal quota calculation error");
-	}
-
-	ctx->bytes_limit = root->storage_limit * 1024;
-	ctx->count_limit = (uint64_t)-1;
-	return ctx;
-}
-
-static int
-dirsize_quota_transaction_commit(struct quota_root_transaction_context *ctx)
+static int 
+dirsize_quota_update(struct quota_root *root __attr_unused__, 
+		     struct quota_transaction_context *ctx __attr_unused__)
 {
-	int ret = ctx->bytes_current == (uint64_t)-1 ? -1 : 0;
-
-	i_free(ctx);
-	return ret;
+	return 0;
 }
 
 struct quota_backend quota_backend_dirsize = {
 	"dirsize",
 
 	{
-		dirsize_quota_init,
+		dirsize_quota_alloc,
+		NULL,
 		dirsize_quota_deinit,
-
-		dirsize_quota_add_storage,
-		dirsize_quota_remove_storage,
-
+		NULL,
 		dirsize_quota_root_get_resources,
-
 		dirsize_quota_get_resource,
-		dirsize_quota_set_resource,
-
-		dirsize_quota_transaction_begin,
-		dirsize_quota_transaction_commit,
-		quota_default_transaction_rollback,
-
-		quota_default_try_alloc,
-		quota_default_try_alloc_bytes,
-		quota_default_test_alloc_bytes,
-		quota_default_alloc,
-		quota_default_free
+		dirsize_quota_update
 	}
 };

Index: quota-fs.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-fs.c,v
retrieving revision 1.18
retrieving revision 1.19
diff -u -d -r1.18 -r1.19
--- quota-fs.c	1 Jul 2006 20:13:49 -0000	1.18
+++ quota-fs.c	30 Jul 2006 17:58:43 -0000	1.19
@@ -46,22 +46,13 @@
 	struct fs_quota_mountpoint *mount;
 };
 
-struct fs_quota_root_iter {
-	struct quota_root_iter iter;
-
-	bool sent;
-};
-
 extern struct quota_backend quota_backend_fs;
 
-static struct quota_root *
-fs_quota_init(struct quota_setup *setup __attr_unused__, const char *name)
+static struct quota_root *fs_quota_alloc(void)
 {
 	struct fs_quota_root *root;
 
 	root = i_new(struct fs_quota_root, 1);
-	root->root.name = i_strdup(name);
-	root->root.v = quota_backend_fs.v;
 	root->uid = geteuid();
 
 	return &root->root;
@@ -89,7 +80,6 @@
 
 	if (root->mount != NULL)
 		fs_quota_mountpoint_free(root->mount);
-	i_free(root->root.name);
 	i_free(root);
 }
 
@@ -110,42 +100,66 @@
 	return mount;
 }
 
-static bool fs_quota_add_storage(struct quota_root *_root,
-				 struct mail_storage *storage)
+static struct fs_quota_root *
+fs_quota_root_find_mountpoint(struct quota *quota,
+			      const struct fs_quota_mountpoint *mount)
+{
+	struct quota_root *const *roots;
+	struct fs_quota_root *empty = NULL;
+	unsigned int i, count;
+
+	roots = array_get(&quota->roots, &count);
+	for (i = 0; i < count; i++) {
+		if (roots[i]->backend == &quota_backend_fs) {
+			struct fs_quota_root *root =
+				(struct fs_quota_root *)roots[i];
+
+			if (root->mount == NULL)
+				empty = root;
+			else if (strcmp(root->mount->mount_path,
+					mount->mount_path) == 0)
+				return root;
+		}
+	}
+	return empty;
+}
+
+static void fs_quota_storage_added(struct quota *quota,
+				   struct mail_storage *storage)
 {
-	struct fs_quota_root *root = (struct fs_quota_root *)_root;
 	struct fs_quota_mountpoint *mount;
+	struct quota_root *_root;
+	struct fs_quota_root *root;
 	const char *dir;
 	bool is_file;
 
 	dir = mail_storage_get_mailbox_path(storage, "", &is_file);
-
-	if (getenv("DEBUG") != NULL)
-		i_info("fs quota add storage dir = %s", dir);
-
 	mount = fs_quota_mountpoint_get(dir);
-	if (root->mount == NULL) {
-		if (mount == NULL) {
-			/* Not found */
-			return TRUE;
-		}
-		root->mount = mount;
-	} else {
-		bool match = strcmp(root->mount->mount_path,
-				    mount->mount_path) == 0;
+	if (getenv("DEBUG") != NULL) {
+		i_info("fs quota add storage dir = %s", dir);
+		i_info("fs quota block device = %s", mount->device_path);
+		i_info("fs quota mount point = %s", mount->mount_path);
+	}
 
+	root = fs_quota_root_find_mountpoint(quota, mount);
+	if (root != NULL && root->mount != NULL) {
+		/* already exists */
 		fs_quota_mountpoint_free(mount);
-		if (!match) {
-			/* different mountpoints, can't use this */
-			return FALSE;
-		}
-		mount = root->mount;
+		return;
 	}
 
-	if (getenv("DEBUG") != NULL) {
-		i_info("fs quota block device = %s", mount->device_path);
-		i_info("fs quota mount point = %s", mount->mount_path);
+	if (root == NULL) {
+		/* create a new root for this mountpoint */
+		_root = quota_root_init(quota, quota_backend_fs.name);
+		root = (struct fs_quota_root *)_root;
+		root->root.name =
+			p_strdup_printf(root->root.pool, "%s%d",
+					quota_backend_fs.name,
+					array_count(&quota->roots));
+	} else {
+		/* this is the default root. */
 	}
+	root->mount = mount;
 
 #ifdef HAVE_Q_QUOTACTL
 	if (mount->path == NULL) {
@@ -155,13 +169,6 @@
 			i_error("open(%s) failed: %m", mount->path);
 	}
 #endif
-	return TRUE;
-}
-
-static void
-fs_quota_remove_storage(struct quota_root *root __attr_unused__,
-			struct mail_storage *storage __attr_unused__)
-{
 }
 
 static const char *const *
@@ -200,8 +207,6 @@
 			     root->uid, (caddr_t)&xdqblk) < 0) {
 			i_error("quotactl(Q_XGETQUOTA, %s) failed: %m",
 				root->mount->device_path);
-			quota_set_error(_root->setup->quota,
-					"Internal quota error");
 			return -1;
 		}
 
@@ -217,8 +222,6 @@
 			     root->uid, (caddr_t)&dqblk) < 0) {
 			i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
 				root->mount->device_path);
-			quota_set_error(_root->setup->quota,
-					"Internal quota error");
 			return -1;
 		}
 
@@ -255,32 +258,10 @@
 	return 1;
 }
 
-static int
-fs_quota_set_resource(struct quota_root *root,
-		      const char *name __attr_unused__,
-		      uint64_t value __attr_unused__)
-{
-	quota_set_error(root->setup->quota, MAIL_STORAGE_ERR_NO_PERMISSION);
-	return -1;
-}
-
-static struct quota_root_transaction_context *
-fs_quota_transaction_begin(struct quota_root *root,
-			   struct quota_transaction_context *ctx)
-{
-	struct quota_root_transaction_context *root_ctx;
-
-	root_ctx = i_new(struct quota_root_transaction_context, 1);
-	root_ctx->root = root;
-	root_ctx->ctx = ctx;
-	root_ctx->disabled = TRUE;
-	return root_ctx;
-}
-
-static int
-fs_quota_transaction_commit(struct quota_root_transaction_context *ctx)
+static int 
+fs_quota_update(struct quota_root *root __attr_unused__,
+		struct quota_transaction_context *ctx __attr_unused__)
 {
-	i_free(ctx);
 	return 0;
 }
 
@@ -288,26 +269,15 @@
 	"fs",
 
 	{
-		fs_quota_init,
+		fs_quota_alloc,
+		NULL,
 		fs_quota_deinit,
 
-		fs_quota_add_storage,
-		fs_quota_remove_storage,
+		fs_quota_storage_added,
 
 		fs_quota_root_get_resources,
-
 		fs_quota_get_resource,
-		fs_quota_set_resource,
-
-		fs_quota_transaction_begin,
-		fs_quota_transaction_commit,
-		quota_default_transaction_rollback,
-
-		quota_default_try_alloc,
-		quota_default_try_alloc_bytes,
-		quota_default_test_alloc_bytes,
-		quota_default_alloc,
-		quota_default_free
+		fs_quota_update
 	}
 };
 

Index: quota-maildir.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-maildir.c,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -d -r1.12 -r1.13
--- quota-maildir.c	24 Jul 2006 01:57:15 -0000	1.12
+++ quota-maildir.c	30 Jul 2006 17:58:43 -0000	1.13
@@ -22,6 +22,7 @@
 struct maildir_quota_root {
 	struct quota_root root;
 
+	const char *maildirsize_path;
 	uint64_t message_bytes_limit;
 	uint64_t message_count_limit;
 
@@ -29,6 +30,7 @@
 	uint64_t total_count;
 
 	int fd;
+	time_t recalc_last_stamp;
 
 	unsigned int master_message_limits:1;
 };
@@ -56,8 +58,8 @@
 	MEMBER(use_excl_lock) FALSE
 };
 
-static int maildir_sum_dir(struct mail_storage *storage, const char *dir,
-			   uint64_t *total_bytes, uint64_t *total_count)
+static int maildir_sum_dir(const char *dir, uint64_t *total_bytes,
+			   uint64_t *total_count)
 {
 	DIR *dirp;
 	struct dirent *dp;
@@ -71,8 +73,7 @@
 	if (dirp == NULL) {
 		if (errno == ENOENT || errno == ESTALE)
 			return 0;
-		mail_storage_set_critical(storage, "opendir(%s) failed: %m",
-					  dir);
+		i_error("opendir(%s) failed: %m", dir);
 		return -1;
 	}
 
@@ -111,16 +112,14 @@
 				*total_bytes += st.st_size;
 				*total_count += 1;
 			} else if (errno != ENOENT && errno != ESTALE) {
-				mail_storage_set_critical(storage,
-					"stat(%s) failed: %m", str_c(path));
+				i_error("stat(%s) failed: %m", str_c(path));
 				ret = -1;
 			}
 		}
 	}
 
 	if (closedir(dirp) < 0) {
-		mail_storage_set_critical(storage, "closedir(%s) failed: %m",
-					  dir);
+		i_error("closedir(%s) failed: %m", dir);
 		return -1;
 	}
 	return ret;
@@ -170,8 +169,7 @@
 		/* ignore if the directory got lost, stale or if it was
 		   actually a file and not a directory */
 		if (errno != ENOENT && errno != ESTALE && errno != ENOTDIR) {
-			mail_storage_set_critical(ctx->ctx->storage,
-				"stat(%s) failed: %m", str_c(ctx->path));
+			i_error("stat(%s) failed: %m", str_c(ctx->path));
 			ctx->state = 0;
 		}
 	}
@@ -209,8 +207,7 @@
 	return ret;
 }
 
-static int maildirsize_write(struct maildir_quota_root *root,
-			     struct mail_storage *storage, const char *path)
+static int maildirsize_write(struct maildir_quota_root *root, const char *path)
 {
 	struct dotlock *dotlock;
 	string_t *str;
@@ -226,8 +223,7 @@
 			return -1;
 		}
 
-		mail_storage_set_critical(storage,
-			"file_dotlock_open(%s) failed: %m", path);
+		i_error("file_dotlock_open(%s) failed: %m", path);
 		return -1;
 	}
 
@@ -246,8 +242,7 @@
 		    (unsigned long long)root->total_bytes,
 		    (unsigned long long)root->total_count);
 	if (write_full(fd, str_data(str), str_len(str)) < 0) {
-		mail_storage_set_critical(storage,
-			"write_full(%s) failed: %m", path);
+		i_error("write_full(%s) failed: %m", path);
 		file_dotlock_delete(&dotlock);
 		return -1;
 	}
@@ -255,38 +250,34 @@
 	/* keep the fd open since we might want to update it later */
 	if (file_dotlock_replace(&dotlock,
 				 DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) < 0) {
-		mail_storage_set_critical(storage,
-			"file_dotlock_replace(%s) failed: %m", path);
+		i_error("file_dotlock_replace(%s) failed: %m", path);
 		return -1;
 	}
 	root->fd = fd;
 	return 0;
 }
 
-static const char *maildirsize_get_path(struct mail_storage *storage)
+static void maildirsize_recalculate_init(struct maildir_quota_root *root)
 {
-	return t_strconcat(mail_storage_get_mailbox_control_dir(storage, ""),
-			   "/"MAILDIRSIZE_FILENAME, NULL);
+	root->total_bytes = root->total_count = 0;
+	root->recalc_last_stamp = 0;
 }
 
-static int maildirsize_recalculate(struct maildir_quota_root *root,
-				   struct mail_storage *storage)
+static int maildirsize_recalculate_storage(struct maildir_quota_root *root,
+					   struct mail_storage *storage)
 {
 	struct maildir_list_context *ctx;
-	const char *dir, *path;
-	time_t mtime, last_stamp = 0;
+	const char *dir;
+	time_t mtime;
 	int ret = 0;
 
-	root->total_bytes = root->total_count = 0;
-
 	ctx = maildir_list_init(storage);
 	while ((dir = maildir_list_next(ctx, &mtime)) != NULL) {
-		if (mtime > last_stamp)
-			last_stamp = mtime;
+		if (mtime > root->recalc_last_stamp)
+			root->recalc_last_stamp = mtime;
 
 		t_push();
-		if (maildir_sum_dir(storage, dir,
-				    &root->total_bytes,
+		if (maildir_sum_dir(dir, &root->total_bytes,
 				    &root->total_count) < 0)
 			ret = -1;
 		t_pop();
@@ -294,27 +285,58 @@
 	if (maildir_list_deinit(ctx) < 0)
 		ret = -1;
 
-	if (ret == 0)
-		ret = maildirs_check_have_changed(storage, last_stamp);
+	return ret;
+}
 
-	t_push();
-	path = maildirsize_get_path(storage);
+static int maildirsize_recalculate_finish(struct maildir_quota_root *root,
+					  int ret)
+{
 	if (ret == 0) {
 		/* maildir didn't change, we can write the maildirsize file */
-		ret = maildirsize_write(root, storage, path);
+		ret = maildirsize_write(root, root->maildirsize_path);
 	}
 	if (ret != 0) {
 		/* make sure it gets rebuilt later */
-		if (unlink(path) < 0 && errno != ENOENT && errno != ESTALE) {
-			mail_storage_set_critical(storage,
-				"unlink(%s) failed: %m", path);
+		if (unlink(root->maildirsize_path) < 0 &&
+		    errno != ENOENT && errno != ESTALE) {
+			i_error("unlink(%s) failed: %m",
+				root->maildirsize_path);
 		}
 	}
-	t_pop();
 
 	return ret;
 }
 
+static int maildirsize_recalculate(struct maildir_quota_root *root)
+{
+	struct mail_storage *const *storages;
+	unsigned int i, count;
+	int ret = 0;
+
+	maildirsize_recalculate_init(root);
+
+	/* count mails from all storages */
+	storages = array_get(&root->root.quota->storages, &count);
+	for (i = 0; i < count; i++) {
+		if (maildirsize_recalculate_storage(root, storages[i]) < 0) {
+			ret = -1;
+			break;
+		}
+	}
+
+	if (ret == 0) {
+		/* check if any of the directories have changed */
+		for (i = 0; i < count; i++) {
+			ret = maildirs_check_have_changed(storages[i],
+						root->recalc_last_stamp);
+			if (ret != 0)
+				break;
+		}
+	}
+
+	return maildirsize_recalculate_finish(root, ret);
+}
+
 static int maildirsize_parse(struct maildir_quota_root *root,
 			     int fd, const char *const *lines)
 {
@@ -397,32 +419,26 @@
 	return 1;
 }
 
-static int maildirsize_read(struct maildir_quota_root *root,
-			    struct mail_storage *storage)
+static int maildirsize_read(struct maildir_quota_root *root)
 {
-	const char *path;
 	char buf[5120+1];
 	unsigned int size;
 	int fd, ret;
 
 	t_push();
-	path = maildirsize_get_path(storage);
 	if (root->fd != -1) {
-		if (close(root->fd) < 0) {
-			mail_storage_set_critical(storage,
-				"close(%s) failed: %m", path);
-		}
+		if (close(root->fd) < 0)
+			i_error("close(%s) failed: %m", root->maildirsize_path);
 		root->fd = -1;
 	}
 
-	fd = nfs_safe_open(path, O_RDWR | O_APPEND);
+	fd = nfs_safe_open(root->maildirsize_path, O_RDWR | O_APPEND);
 	if (fd == -1) {
 		if (errno == ENOENT)
 			ret = 0;
 		else {
 			ret = -1;
-			mail_storage_set_critical(storage,
-				"open(%s) failed: %m", path);
+			i_error("open(%s) failed: %m", root->maildirsize_path);
 		}
 		t_pop();
 		return ret;
@@ -435,8 +451,7 @@
 		if (ret < 0) {
 			if (errno == ESTALE)
 				break;
-			mail_storage_set_critical(storage, "read(%s) failed: %m",
-						  path);
+			i_error("read(%s) failed: %m", root->maildirsize_path);
 		}
 		size += ret;
 	}
@@ -467,12 +482,11 @@
 	return ret;
 }
 
-static int maildirquota_refresh(struct maildir_quota_root *root,
-				struct mail_storage *storage)
+static int maildirquota_refresh(struct maildir_quota_root *root)
 {
 	int ret;
 
-	ret = maildirsize_read(root, storage);
+	ret = maildirsize_read(root);
 	if (ret == 0) {
 		if (root->message_bytes_limit == (uint64_t)-1 &&
 		    root->message_count_limit == (uint64_t)-1) {
@@ -480,13 +494,12 @@
 			return 0;
 		}
 
-		ret = maildirsize_recalculate(root, storage);
+		ret = maildirsize_recalculate(root);
 	}
 	return ret < 0 ? -1 : 0;
 }
 
 static int maildirsize_update(struct maildir_quota_root *root,
-			      struct mail_storage *storage,
 			      int count_diff, int64_t bytes_diff)
 {
 	const char *str;
@@ -508,47 +521,22 @@
 		if (errno == ESTALE) {
 			/* deleted/replaced already, ignore */
 		} else {
-			mail_storage_set_critical(storage,
-				"write_full(%s) failed: %m",
-				maildirsize_get_path(storage));
+			i_error("write_full(%s) failed: %m",
+				root->maildirsize_path);
 		}
 	}
 	t_pop();
 	return ret;
 }
 
-static struct quota_root *
-maildir_quota_init(struct quota_setup *setup, const char *name __attr_unused__)
+static struct quota_root *maildir_quota_alloc(void)
 {
 	struct maildir_quota_root *root;
-	const char *const *args;
-	unsigned long long size;
 
 	root = i_new(struct maildir_quota_root, 1);
-	root->root.name = i_strdup(name);
-	root->root.v = quota_backend_maildir.v;
 	root->fd = -1;
 	root->message_bytes_limit = (uint64_t)-1;
 	root->message_count_limit = (uint64_t)-1;
-
-	t_push();
-	args = t_strsplit(setup->data, ":");
-
-	for (; *args != '\0'; args++) {
-		if (strncmp(*args, "storage=", 8) == 0) {
-			size = strtoull(*args + 8, NULL, 10) * 1024;
-			if (size != 0)
-				root->message_bytes_limit = size;
-			root->master_message_limits = TRUE;
-		} else if (strncmp(*args, "messages=", 9) == 0) {
-			size = strtoull(*args + 9, NULL, 10);
-			if (size != 0)
-				root->message_count_limit = size;
-			root->master_message_limits = TRUE;
-		}
-	}
-	t_pop();
-
 	return &root->root;
 }
 
@@ -556,28 +544,45 @@
 {
 	struct maildir_quota_root *root = (struct maildir_quota_root *)_root;
 
-	i_free(root->root.name);
 	i_free(root);
 }
 
-static bool
-maildir_quota_add_storage(struct quota_root *root __attr_unused__,
-			  struct mail_storage *_storage)
+static void
+maildir_quota_root_storage_added(struct quota_root *_root,
+				 struct mail_storage *storage)
 {
-	if (strcmp(_storage->name, "maildir") == 0) {
-		struct maildir_storage *storage =
-			(struct maildir_storage *)_storage;
+	struct maildir_quota_root *root = (struct maildir_quota_root *)_root;
+	const char *control_dir;
 
-		/* For newly generated filenames add ,S=size. */
-		storage->save_size_in_filename = TRUE;
-	}
-	return TRUE;
+	if (root->maildirsize_path != NULL)
+		return;
+
+	control_dir = mail_storage_get_mailbox_control_dir(storage, "");
+	root->maildirsize_path =
+		p_strconcat(_root->pool, control_dir,
+			    "/"MAILDIRSIZE_FILENAME, NULL);
 }
 
 static void
-maildir_quota_remove_storage(struct quota_root *root __attr_unused__,
-			     struct mail_storage *storage __attr_unused__)
+maildir_quota_storage_added(struct quota *quota,
+			    struct mail_storage *_storage)
 {
+	struct maildir_storage *storage =
+		(struct maildir_storage *)_storage;
+	struct quota_root **roots;
+	unsigned int i, count;
+
+	if (strcmp(_storage->name, "maildir") != 0)
+		return;
+
+	roots = array_get_modifiable(&quota->roots, &count);
+	for (i = 0; i < count; i++) {
+		if (roots[i]->backend == &quota_backend_maildir)
+			maildir_quota_root_storage_added(roots[i], _storage);
+	}
+
+	/* For newly generated filenames add ,S=size. */
+	storage->save_size_in_filename = TRUE;
 }
 
 static const char *const *
@@ -592,120 +597,49 @@
 	return resources_both;
 }
 
-static struct mail_storage *
-maildir_quota_root_get_storage(struct quota_root *root)
-{
-	/* FIXME: figure out how to support multiple storages */
-	struct mail_storage *const *storages;
-	unsigned int count;
-
-	storages = array_get(&root->storages, &count);
-	i_assert(count > 0);
-
-	return storages[0];
-}
-
 static int
 maildir_quota_get_resource(struct quota_root *_root, const char *name,
-			   uint64_t *value_r, uint64_t *limit_r)
+			   uint64_t *value_r, uint64_t *limit  __attr_unused__)
 {
 	struct maildir_quota_root *root = (struct maildir_quota_root *)_root;
 
-	if (maildirquota_refresh(root,
-				 maildir_quota_root_get_storage(_root)) < 0)
+	if (maildirquota_refresh(root) < 0)
 		return -1;
 
-	if (strcmp(name, QUOTA_NAME_STORAGE) == 0) {
-		if (root->message_bytes_limit == (uint64_t)-1)
-			return 0;
-
-		*limit_r = root->message_bytes_limit / 1024;
+	if (strcmp(name, QUOTA_NAME_STORAGE) == 0)
 		*value_r = root->total_bytes / 1024;
-	} else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) {
-		if (root->message_count_limit == (uint64_t)-1)
-			return 0;
-
-		*limit_r = root->message_count_limit;
+	else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0)
 		*value_r = root->total_count;
-	} else {
+	else
 		return 0;
-	}
 	return 1;
 }
 
 static int
-maildir_quota_set_resource(struct quota_root *root,
-			   const char *name __attr_unused__,
-			   uint64_t value __attr_unused__)
-{
-	quota_set_error(root->setup->quota, MAIL_STORAGE_ERR_NO_PERMISSION);
-	return -1;
-}
-
-static struct quota_root_transaction_context *
-maildir_quota_transaction_begin(struct quota_root *_root,
-				struct quota_transaction_context *_ctx)
-{
-	struct maildir_quota_root *root = (struct maildir_quota_root *)_root;
-	struct quota_root_transaction_context *ctx;
-
-	ctx = i_new(struct quota_root_transaction_context, 1);
-	ctx->root = _root;
-	ctx->ctx = _ctx;
-
-	if (maildirquota_refresh(root,
-				 maildir_quota_root_get_storage(_root)) < 0) {
-		/* failed calculating the current quota */
-		ctx->bytes_current = (uint64_t)-1;
-	} else {
-		ctx->bytes_limit = root->message_bytes_limit;
-		ctx->count_limit = root->message_count_limit;
-		ctx->bytes_current = root->total_bytes;
-		ctx->count_current = root->total_count;
-	}
-	return ctx;
-}
-
-static int
-maildir_quota_transaction_commit(struct quota_root_transaction_context *ctx)
+maildir_quota_update(struct quota_root *_root,
+		     struct quota_transaction_context *ctx)
 {
 	struct maildir_quota_root *root =
-		(struct maildir_quota_root *)ctx->root;
-	int ret = ctx->bytes_current == (uint64_t)-1 ? -1 : 0;
+		(struct maildir_quota_root *) _root;
 
-	if (root->fd != -1 && ret == 0) {
+	if (root->fd != -1) {
 		/* if writing fails, we don't care all that much */
-		(void)maildirsize_update(root,
-				maildir_quota_root_get_storage(ctx->root),
-				ctx->count_diff, ctx->bytes_diff);
+		(void)maildirsize_update(root, ctx->count_used,
+					 ctx->bytes_used);
 	}
-	i_free(ctx);
-	return ret;
+	return 0;
 }
 
 struct quota_backend quota_backend_maildir = {
 	"maildir",
 
 	{
-		maildir_quota_init,
+		maildir_quota_alloc,
+		NULL,
 		maildir_quota_deinit,
-
-		maildir_quota_add_storage,
-		maildir_quota_remove_storage,
-
+		maildir_quota_storage_added,
 		maildir_quota_root_get_resources,
-
 		maildir_quota_get_resource,
-		maildir_quota_set_resource,
-
-		maildir_quota_transaction_begin,
-		maildir_quota_transaction_commit,
-		quota_default_transaction_rollback,
-
-		quota_default_try_alloc,
-		quota_default_try_alloc_bytes,
-		quota_default_test_alloc_bytes,
-		quota_default_alloc,
-		quota_default_free
+		maildir_quota_update
 	}
 };

Index: quota-plugin.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-plugin.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- quota-plugin.c	1 Mar 2006 09:15:03 -0000	1.3
+++ quota-plugin.c	30 Jul 2006 17:58:43 -0000	1.4
@@ -1,4 +1,4 @@
-/* Copyright (C) 2005 Timo Sirainen */
+/* Copyright (C) 2005 Timo Sirainen & Tianyan Liu */
 
 #include "lib.h"
 #include "mail-storage.h"
@@ -14,20 +14,68 @@
 
 struct quota *quota;
 
+static void quota_root_add_rules(const char *root_name, 
+				 struct quota_root *root)
+{
+	const char *rule_name, *rule, *error;
+	unsigned int i;
+
+	t_push();
+
+	rule_name = t_strconcat(root_name, "_RULE", NULL);
+	for (i = 2;; i++) {
+		rule = getenv(rule_name);
+
+		if (rule == NULL)
+			break;
+
+		if (quota_root_add_rule(root, rule, &error) < 0) {
+			i_fatal("Quota root %s: Invalid rule: %s",
+				root_name, rule);
+		}
+		rule_name = t_strdup_printf("%s_RULE%d", root_name, i);
+	}
+
+	t_pop();
+}
+
 void quota_plugin_init(void)
 {
+	struct quota_root *root;
+	unsigned int i;
 	const char *env;
 
 	env = getenv("QUOTA");
-	if (env != NULL) {
-		quota = quota_init();
-		/* Currently we support only one quota setup */
-		(void)quota_setup_init(quota, env, TRUE);
+	if (env == NULL)
+		return;
 
-		quota_next_hook_mail_storage_created =
-			hook_mail_storage_created;
-		hook_mail_storage_created = quota_mail_storage_created;
+	quota = quota_init();
+
+	root = quota_root_init(quota, env);
+	if (root == NULL)
+		i_fatal("Couldn't create quota root: %s", env);
+	quota_root_add_rules("QUOTA", root);
+
+	t_push();
+	for (i = 2;; i++) {
+		const char *root_name;
+
+		root_name = t_strdup_printf("QUOTA%d", i);
+		env = getenv(root_name);
+
+		if (env == NULL)
+			break;
+
+		root = quota_root_init(quota, env);
+		if (root == NULL)
+			i_fatal("Couldn't create quota root: %s", env);
+		quota_root_add_rules(root_name, root);
 	}
+	t_pop();
+
+	quota_next_hook_mail_storage_created =
+		hook_mail_storage_created;
+	hook_mail_storage_created = quota_mail_storage_created;
 }
 
 void quota_plugin_deinit(void)

Index: quota-private.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-private.h,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -d -r1.10 -r1.11
--- quota-private.h	28 Jun 2006 13:11:00 -0000	1.10
+++ quota-private.h	30 Jul 2006 17:58:43 -0000	1.11
@@ -9,54 +9,27 @@
 extern unsigned int quota_module_id;
 
 struct quota {
-	ARRAY_DEFINE(setups, struct quota_setup *);
-	char *last_error;
-};
-
-struct quota_setup {
-	struct quota *quota;
-
-	struct quota_backend *backend;
-	char *data;
-
-	/* List of quota roots. It's array because there shouldn't be many. */
 	ARRAY_DEFINE(roots, struct quota_root *);
-
-	unsigned int user_root:1;
+	ARRAY_DEFINE(storages, struct mail_storage *);
 };
 
 struct quota_backend_vfuncs {
-	struct quota_root *(*init)(struct quota_setup *setup, const char *name);
+	struct quota_root *(*alloc)(void);
+	int (*init)(struct quota_root *root, const char *args);
 	void (*deinit)(struct quota_root *root);
 
-	bool (*add_storage)(struct quota_root *root,
-			    struct mail_storage *storage);
-	void (*remove_storage)(struct quota_root *root,
-			       struct mail_storage *storage);
+	/* called once for each backend */
+	void (*storage_added)(struct quota *quota,
+			      struct mail_storage *storage);
 
 	const char *const *(*get_resources)(struct quota_root *root);
+	/* the limit is set by default, so it shouldn't normally need to
+	   be changed. */
 	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_root_transaction_context *
-		(*transaction_begin)(struct quota_root *root,
-				     struct quota_transaction_context *ctx);
-	int (*transaction_commit)(struct quota_root_transaction_context *ctx);
-	void (*transaction_rollback)
-		(struct quota_root_transaction_context *ctx);
+			    uint64_t *value_r, uint64_t *limit);
 
-	int (*try_alloc)(struct quota_root_transaction_context *ctx,
-			 struct mail *mail, bool *too_large_r);
-	int (*try_alloc_bytes)(struct quota_root_transaction_context *ctx,
-			       uoff_t size, bool *too_large_r);
-	int (*test_alloc_bytes)(struct quota_root_transaction_context *ctx,
-				uoff_t size, bool *too_large_r);
-	void (*alloc)(struct quota_root_transaction_context *ctx,
-		      struct mail *mail);
-	void (*free)(struct quota_root_transaction_context *ctx,
-		     struct mail *mail);
+	int (*update)(struct quota_root *root, 
+		      struct quota_transaction_context *ctx);
 };
 
 struct quota_backend {
@@ -64,72 +37,46 @@
 	struct quota_backend_vfuncs v;
 };
 
+struct quota_rule {
+	char *mailbox_name;
+
+	int64_t bytes_limit, count_limit;
+};
+
 struct quota_root {
-	struct quota_setup *setup;
+	pool_t pool;
 
 	/* Unique quota root name. */
 	char *name;
 
-	struct quota_backend_vfuncs v;
+	/* pointer to the quota that owns this root */
+	struct quota *quota;
+
+	struct quota_backend *backend;
+	struct quota_rule default_rule;
+	ARRAY_DEFINE(rules, struct quota_rule);
 
-	/* Mail storages using this quota root. */
-	ARRAY_DEFINE(storages, struct mail_storage *);
 	/* Module-specific contexts. See quota_module_id. */
 	ARRAY_DEFINE(quota_module_contexts, void);
-
-	unsigned int user_root:1;
-};
-
-struct quota_root_iter {
-	struct quota_mail_storage *qstorage;
-	unsigned int idx;
 };
 
 struct quota_transaction_context {
 	struct quota *quota;
+	struct mailbox *box;
 
-	ARRAY_DEFINE(root_transactions,
-		     struct quota_root_transaction_context *);
-	struct mail *mail;
-};
-
-struct quota_root_transaction_context {
-	struct quota_root *root;
-	struct quota_transaction_context *ctx;
-
-	int count_diff;
-	int64_t bytes_diff;
+	int64_t bytes_used, count_used;
+	uint64_t bytes_left, count_left;
 
-	uint64_t bytes_limit, count_limit;
-	uint64_t bytes_current, count_current;
+	struct mail *tmp_mail;
 
-	unsigned int disabled:1;
+	unsigned int failed:1;
 };
 
 /* Register storage to all user's quota roots. */
 void quota_add_user_storage(struct quota *quota, struct mail_storage *storage);
-
-/* Likn root and storage together. Returns TRUE if successful, FALSE if it
-   can't be done (eg. different filesystems with filesystem quota) */
-bool quota_mail_storage_add_root(struct mail_storage *storage,
-				 struct quota_root *root);
-void quota_mail_storage_remove_root(struct mail_storage *storage,
-				    struct quota_root *root);
+void quota_remove_user_storage(struct quota *quota, 
+			       struct mail_storage *storage);
 
 void quota_set_error(struct quota *quota, const char *errormsg);
 
-/* default simple implementations for bytes/count updating */
-void
-quota_default_transaction_rollback(struct quota_root_transaction_context *ctx);
-int quota_default_try_alloc(struct quota_root_transaction_context *ctx,
-			    struct mail *mail, bool *too_large_r);
-int quota_default_try_alloc_bytes(struct quota_root_transaction_context *ctx,
-				  uoff_t size, bool *too_large_r);
-int quota_default_test_alloc_bytes(struct quota_root_transaction_context *ctx,
-				   uoff_t size, bool *too_large_r);
-void quota_default_alloc(struct quota_root_transaction_context *ctx,
-			 struct mail *mail);
-void quota_default_free(struct quota_root_transaction_context *ctx,
-			struct mail *mail);
-
 #endif

Index: quota-storage.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota-storage.c,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -d -r1.10 -r1.11
--- quota-storage.c	28 Jun 2006 13:11:00 -0000	1.10
+++ quota-storage.c	30 Jul 2006 17:58:43 -0000	1.11
@@ -16,10 +16,6 @@
 
 struct quota_mail_storage {
 	struct mail_storage_vfuncs super;
-	struct quota *quota;
-
-	/* List of quota roots this storage belongs to. */
-	ARRAY_DEFINE(roots, struct quota_root *);
 };
 
 struct quota_mailbox {
@@ -58,7 +54,7 @@
 	struct quota_transaction_context *qt;
 
 	t = qbox->super.transaction_begin(box, flags);
-	qt = quota_transaction_begin(box);
+	qt = quota_transaction_begin(quota, box);
 
 	array_idx_set(&t->module_contexts, quota_storage_module_id, &qt);
 	return t;
@@ -76,8 +72,8 @@
 		return -1;
 	} else {
 		(void)quota_transaction_commit(qt);
-		if (qt->mail != NULL)
-			mail_free(&qt->mail);
+		if (qt->tmp_mail != NULL)
+			mail_free(&qt->tmp_mail);
 		return 0;
 	}
 }
@@ -90,8 +86,8 @@
 
 	qbox->super.transaction_rollback(ctx);
 
-	if (qt->mail != NULL)
-		mail_free(&qt->mail);
+	if (qt->tmp_mail != NULL)
+		mail_free(&qt->tmp_mail);
 	quota_transaction_rollback(qt);
 }
 
@@ -129,8 +125,8 @@
 		mail_storage_set_error(t->box->storage, "Quota exceeded");
 		return -1;
 	} else {
-		mail_storage_set_error(t->box->storage,  "%s",
-				       quota_last_error(quota));
+		mail_storage_set_error(t->box->storage,
+				       "Internal quota calculation error");
 		return -1;
 	}
 }
@@ -145,11 +141,11 @@
 
 	if (dest_mail == NULL) {
 		/* we always want to know the mail size */
-		if (qt->mail == NULL) {
-			qt->mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE,
-					      NULL);
+		if (qt->tmp_mail == NULL) {
+			qt->tmp_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE,
+						  NULL);
 		}
-		dest_mail = qt->mail;
+		dest_mail = qt->tmp_mail;
 	}
 
 	qbox->save_hack = FALSE;
@@ -186,25 +182,25 @@
 		   full mail. */
 		bool too_large;
 
-		ret = quota_test_alloc_bytes(qt, st->st_size, &too_large);
+		ret = quota_test_alloc(qt, st->st_size, &too_large);
 		if (ret == 0) {
 			mail_storage_set_error(t->box->storage,
 					       "Quota exceeded");
 			return -1;
 		} else if (ret < 0) {
-			mail_storage_set_error(t->box->storage,  "%s",
-					       quota_last_error(quota));
+			mail_storage_set_error(t->box->storage,
+				"Internal quota calculation error");
 			return -1;
 		}
 	}
 
 	if (dest_mail == NULL) {
 		/* we always want to know the mail size */
-		if (qt->mail == NULL) {
-			qt->mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE,
-					      NULL);
+		if (qt->tmp_mail == NULL) {
+			qt->tmp_mail = mail_alloc(t, MAIL_FETCH_PHYSICAL_SIZE,
+						  NULL);
 		}
-		dest_mail = qt->mail;
+		dest_mail = qt->tmp_mail;
 	}
 
 	return qbox->super.save_init(t, flags, keywords, received_date,
@@ -214,13 +210,15 @@
 
 static int quota_save_finish(struct mail_save_context *ctx)
 {
+	struct quota_transaction_context *qt = QUOTA_CONTEXT(ctx->transaction);
 	struct quota_mailbox *qbox = QUOTA_CONTEXT(ctx->transaction->box);
 
 	if (qbox->super.save_finish(ctx) < 0)
 		return -1;
 
 	qbox->save_hack = TRUE;
-	return quota_check(ctx->transaction, ctx->dest_mail);
+	return quota_check(ctx->transaction, ctx->dest_mail != NULL ?
+			   ctx->dest_mail : qt->tmp_mail);
 }
 
 static struct mailbox *
@@ -295,22 +293,8 @@
 static void quota_storage_destroy(struct mail_storage *storage)
 {
 	struct quota_mail_storage *qstorage = QUOTA_CONTEXT(storage);
-	struct quota_root *const *roots;
-	struct mail_storage *const *storages;
-	unsigned int i, j, root_count, storage_count;
 
-	/* remove the storage from all roots' storages list */
-	roots = array_get(&qstorage->roots, &root_count);
-	for (i = 0; i < root_count; i++) {
-		storages = array_get(&roots[i]->storages, &storage_count);
-		for (j = 0; j < storage_count; j++) {
-			if (storages[j] == storage) {
-				array_delete(&roots[i]->storages, j, 1);
-				break;
-			}
-		}
-		i_assert(j != storage_count);
-	}
+	quota_remove_user_storage(quota, storage);
 
 	qstorage->super.destroy(storage);
 }
@@ -328,8 +312,6 @@
 	storage->v.mailbox_open = quota_mailbox_open;
 	storage->v.mailbox_delete = quota_mailbox_delete;
 
-	ARRAY_CREATE(&qstorage->roots, storage->pool, struct quota_root *, 4);
-
 	if (!quota_storage_module_id_set) {
 		quota_storage_module_id = mail_storage_module_id++;
 		quota_storage_module_id_set = TRUE;
@@ -343,74 +325,3 @@
 		quota_add_user_storage(quota, storage);
 	}
 }
-
-bool quota_mail_storage_add_root(struct mail_storage *storage,
-				 struct quota_root *root)
-{
-	struct quota_mail_storage *qstorage = QUOTA_CONTEXT(storage);
-
-	if (!root->v.add_storage(root, storage))
-		return FALSE;
-
-	array_append(&root->storages, &storage, 1);
-	array_append(&qstorage->roots, &root, 1);
-	return TRUE;
-}
-
-void quota_mail_storage_remove_root(struct mail_storage *storage,
-				    struct quota_root *root)
-{
-	struct quota_mail_storage *qstorage = QUOTA_CONTEXT(storage);
-	struct mail_storage *const *storages;
-	struct quota_root *const *roots;
-	unsigned int i, count;
-
-	storages = array_get(&root->storages, &count);
-	for (i = 0; i < count; i++) {
-		if (storages[i] == storage) {
-			array_delete(&root->storages, i, 1);
-			break;
-		}
-	}
-	i_assert(i != count);
-
-	roots = array_get(&qstorage->roots, &count);
-	for (i = 0; i < count; i++) {
-		if (roots[i] == root) {
-			array_delete(&qstorage->roots, i, 1);
-			break;
-		}
-	}
-	i_assert(i != count);
-
-	root->v.remove_storage(root, storage);
-}
-
-struct quota_root_iter *quota_root_iter_init(struct mailbox *box)
-{
-	struct quota_mail_storage *qstorage = QUOTA_CONTEXT(box->storage);
-	struct quota_root_iter *iter;
-
-	iter = i_new(struct quota_root_iter, 1);
-	iter->qstorage = qstorage;
-	return iter;
-}
-
-struct quota_root *quota_root_iter_next(struct quota_root_iter *iter)
-{
-	struct quota_root *const *roots;
-	unsigned int count;
-
-	roots = array_get(&iter->qstorage->roots, &count);
-	i_assert(iter->idx <= count);
-
-	if (iter->idx >= count)
-		return NULL;
-
-	return roots[iter->idx++];
-}
-
-void quota_root_iter_deinit(struct quota_root_iter *iter)
-{
-	i_free(iter);
-}

Index: quota.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota.c,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -d -r1.11 -r1.12
--- quota.c	28 Jun 2006 13:11:00 -0000	1.11
+++ quota.c	30 Jul 2006 17:58:43 -0000	1.12
@@ -5,6 +5,16 @@
 #include "hash.h"
 #include "quota-private.h"
 #include "quota-fs.h"
+#include <stdlib.h>
+
+#define RULE_NAME_ALL_MAILBOXES "*"
+
+struct quota_root_iter {
+	struct quota *quota;
+	struct mailbox *box;
+
+	unsigned int i;
+};
 
 unsigned int quota_module_id = 0;
 
@@ -30,98 +40,92 @@
 	struct quota *quota;
 
 	quota = i_new(struct quota, 1);
-	ARRAY_CREATE(&quota->setups, default_pool, struct quota_setup *, 4);
+	ARRAY_CREATE(&quota->roots, default_pool, struct quota_root *, 4);
+	ARRAY_CREATE(&quota->storages, default_pool, struct mail_storage *, 8);
+
 	return quota;
 }
 
 void quota_deinit(struct quota *quota)
 {
-	while (array_count(&quota->setups) > 0) {
-		struct quota_setup *const *setup;
+	struct quota_root **root;
 
-		setup = array_idx(&quota->setups, 0);
-		quota_setup_deinit(*setup);
+	while (array_count(&quota->roots) > 0) {
+		root = array_idx_modifiable(&quota->roots, 0);
+		quota_root_deinit(*root);
 	}
 
-	array_free(&quota->setups);
+	array_free(&quota->roots);
+	array_free(&quota->storages);
 	i_free(quota);
 }
 
-struct quota_setup *
-quota_setup_init(struct quota *quota, const char *data, bool user_root)
+static struct quota_backend *quota_backend_find(const char *name)
 {
-	struct quota_setup *setup;
-	const char *backend_name, *p;
 	unsigned int i;
 
-	setup = i_new(struct quota_setup, 1);
-	setup->quota = quota;
-	setup->data = i_strdup(data);
-	setup->user_root = user_root;
-	ARRAY_CREATE(&setup->roots, default_pool, struct quota_root *, 4);
+	for (i = 0; i < QUOTA_CLASS_COUNT; i++) {
+		if (strcmp(quota_backends[i]->name, name) == 0)
+			return quota_backends[i];
+	}
+
+	return NULL;
+}
+
+struct quota_root *quota_root_init(struct quota *quota, const char *root_def)
+{
+	struct quota_root *root;
+	struct quota_backend *backend;
+	const char *p, *args, *backend_name;
 
 	t_push();
-	p = strchr(setup->data, ':');
+
+	/* <backend>[:<quota root name>[:<backend args>]] */
+	p = strchr(root_def, ':');
 	if (p == NULL) {
-		backend_name = setup->data;
-		data = "";
+		backend_name = root_def;
+		args = NULL;
 	} else {
-		backend_name = t_strdup_until(setup->data, p);
-		data = p+1;
-	}
-	for (i = 0; i < QUOTA_CLASS_COUNT; i++) {
-		if (strcmp(quota_backends[i]->name, backend_name) == 0) {
-			setup->backend = quota_backends[i];
-			break;
-		}
+		backend_name = t_strdup_until(root_def, p);
+		args = p + 1;
 	}
 
-	if (setup->backend == NULL)
+	backend = quota_backend_find(backend_name);
+	if (backend == NULL)
 		i_fatal("Unknown quota backend: %s", backend_name);
-
+	
 	t_pop();
 
-	array_append(&quota->setups, &setup, 1);
-	return setup;
-}
-
-void quota_setup_deinit(struct quota_setup *setup)
-{
-	struct quota_setup *const *setups;
-	unsigned int i, count;
+	root = backend->v.alloc();
+	root->quota = quota;
+	root->backend = backend;
+	root->pool = pool_alloconly_create("quota root", 512);
 
-	setups = array_get(&setup->quota->setups, &count);
-	for (i = 0; i < count; i++) {
-		if (setups[i] == setup) {
-			array_delete(&setup->quota->setups, i, 1);
-			break;
+	if (args != NULL) {
+		/* save root's name */
+		p = strchr(args, ':');
+		if (p == NULL) {
+			root->name = p_strdup(root->pool, args);
+			args = NULL;
+		} else {
+			root->name = p_strdup_until(root->pool, args, p);
+			args = p + 1;
 		}
-	}
-	i_assert(i != count);
-
-	while (array_count(&setup->roots) > 0) {
-		struct quota_root *const *root;
-
-		root = array_idx(&setup->roots, 0);
-		quota_root_deinit(*root);
+	} else {
+		root->name = "";
 	}
 
-	array_free(&setup->roots);
-	i_free(setup->data);
-	i_free(setup);
-}
+	ARRAY_CREATE(&root->rules, default_pool, struct quota_rule, 4);
+	ARRAY_CREATE(&root->quota_module_contexts, default_pool, void *, 5);
 
-struct quota_root *
-quota_root_init(struct quota_setup *setup, const char *name)
-{
-	struct quota_root *root;
+	array_append(&quota->roots, &root, 1);
 
-	root = setup->backend->v.init(setup, name);
-	root->setup = setup;
-	ARRAY_CREATE(&root->storages, default_pool, struct mail_storage *, 8);
-	array_create(&root->quota_module_contexts,
-		     default_pool, sizeof(void *), 5);
-	array_append(&setup->roots, &root, 1);
+	if (backend->v.init != NULL) {
+		if (backend->v.init(root, args) < 0) {
+			quota_root_deinit(root);
+			return NULL;
+		}
+	}
 
 	if (hook_quota_root_created != NULL)
 		hook_quota_root_created(root);
@@ -130,341 +134,382 @@
 
 void quota_root_deinit(struct quota_root *root)
 {
-	/* make a copy, since root is freed */
-	struct array module_contexts = root->quota_module_contexts.arr;
-	struct mail_storage *const *storage_p;
+	pool_t pool = root->pool;
 	struct quota_root *const *roots;
 	unsigned int i, count;
 
-	/* remove from all storages */
-	while (array_count(&root->storages) > 0) {
-		storage_p = array_idx(&root->storages, 0);
-		quota_mail_storage_remove_root(*storage_p, root);
+	roots = array_get(&root->quota->roots, &count);
+	for (i = 0; i < count; i++) {
+		if (roots[i] == root)
+			array_delete(&root->quota->roots, i, 1);
 	}
 
-	/* remove from setup */
-	roots = array_get(&root->setup->roots, &count);
+	array_free(&root->rules);
+	array_free(&root->quota_module_contexts);
+
+	root->backend->v.deinit(root);
+	pool_unref(pool);
+}
+
+static struct quota_rule *
+quota_root_rule_find(struct quota_root *root, const char *name)
+{
+	struct quota_rule *rules;
+	unsigned int i, count;
+
+	rules = array_get_modifiable(&root->rules, &count);
 	for (i = 0; i < count; i++) {
-		if (roots[i] == root) {
-			array_delete(&root->setup->roots, i, 1);
-			break;
-		}
+		if (strcmp(rules[i].mailbox_name, name) == 0)
+			return &rules[i];
 	}
-	i_assert(i != count);
-
-	array_free(&root->storages);
-	root->v.deinit(root);
-	_array_free(&module_contexts);
+	return NULL;
 }
 
-void quota_add_user_storage(struct quota *quota, struct mail_storage *storage)
+int quota_root_add_rule(struct quota_root *root, const char *rule_def,
+			const char **error_r)
 {
-	struct quota_setup *const *setups;
-	struct quota_root *const *roots;
-	unsigned int i, j, setup_count, root_count;
-	bool found = FALSE;
+	struct quota_rule *rule;
+	const char **args;
+	int ret = 0;
 
-	setups = array_get(&quota->setups, &setup_count);
-	for (i = 0; i < setup_count; i++) {
-		roots = array_get(&setups[i]->roots, &root_count);
-		for (j = 0; j < root_count; j++) {
-			if (!roots[j]->user_root)
-				continue;
+	if (*rule_def == '\0') {
+		*error_r = "Empty rule";
+		return -1;
+	}
 
-			if (quota_mail_storage_add_root(storage, roots[j]))
-				found = TRUE;
+	/* <mailbox name>:<quota limits> */
+	t_push();
+        args = t_strsplit(rule_def, ":");
+
+	rule = quota_root_rule_find(root, *args);
+	if (rule == NULL) {
+		if (strcmp(*args, RULE_NAME_ALL_MAILBOXES) == 0)
+			rule = &root->default_rule;
+		else {
+			rule = array_append_space(&root->rules);
+			rule->mailbox_name = p_strdup(root->pool, *args);
 		}
 	}
 
-	if (!found && setup_count > 0) {
-		/* create a new quota root for the storage */
-		struct quota_root *root;
+	for (args++; *args != NULL; args++) {
+		if (strncmp(*args, "storage=", 8) == 0)
+			rule->bytes_limit = strtoll(*args + 8, NULL, 10) * 1024;
+		else if (strncmp(*args, "messages=", 9) == 0)
+			rule->count_limit = strtoll(*args + 9, NULL, 10);
+		else {
+			*error_r = p_strdup_printf(root->pool,
+					"Invalid rule limit: %s", *args);
+			ret = -1;
+			break;
+		}
+	}
+	t_pop();
+	return ret;
+}
 
-		root = quota_root_init(setups[0], ""); // FIXME: name?
-		found = quota_mail_storage_add_root(storage, root);
-		i_assert(found);
+static bool quota_root_get_rule_limits(struct quota_root *root,
+				       const char *mailbox_name,
+				       uint64_t *bytes_limit_r,
+				       uint64_t *count_limit_r)
+{
+	struct quota_rule *rule;
+	int64_t bytes_limit, count_limit;
+	bool found;
+
+	bytes_limit = root->default_rule.bytes_limit;
+	count_limit = root->default_rule.count_limit;
+
+	/* if default rule limits are 0, this rule applies only to specific
+	   mailboxes */
+	found = bytes_limit != 0 || count_limit != 0;
+
+	rule = quota_root_rule_find(root, mailbox_name);
+	if (rule != NULL) {
+		bytes_limit += rule->bytes_limit;
+		count_limit += rule->count_limit;
+		found = TRUE;
 	}
+
+	*bytes_limit_r = bytes_limit <= 0 ? 0 : bytes_limit;
+	*count_limit_r = count_limit <= 0 ? 0 : count_limit;
+	return found;
 }
 
-struct quota_root *quota_root_lookup(struct quota *quota, const char *name)
+void quota_add_user_storage(struct quota *quota, struct mail_storage *storage)
 {
-	struct quota_setup *const *setups;
 	struct quota_root *const *roots;
-	unsigned int i, j, setup_count, root_count;
+	struct quota_backend **backends;
+	unsigned int i, j, count;
 
-	setups = array_get(&quota->setups, &setup_count);
-	for (i = 0; i < setup_count; i++) {
-		roots = array_get(&setups[i]->roots, &root_count);
-		for (j = 0; j < root_count; j++) {
-			if (strcmp(roots[j]->name, name) == 0)
-				return roots[j];
+	array_append(&quota->storages, &storage, 1);
+
+	roots = array_get(&quota->roots, &count);
+	/* @UNSAFE: get different backends into one array */
+	backends = t_new(struct quota_backend *, count + 1);
+	for (i = 0; i < count; i++) {
+		for (j = 0; backends[j] != NULL; j++) {
+			if (backends[j] == roots[i]->backend)
+				break;
 		}
+		if (backends[j] == NULL)
+			backends[j] = roots[i]->backend;
 	}
-	return NULL;
-}
 
-const char *quota_root_get_name(struct quota_root *root)
-{
-	return root->name;
+	for (i = 0; backends[i] != NULL; i++) {
+		if (backends[i]->v.storage_added != NULL)
+			backends[i]->v.storage_added(quota, storage);
+	}
 }
 
-const char *const *quota_root_get_resources(struct quota_root *root)
+void quota_remove_user_storage(struct quota *quota,
+			       struct mail_storage *storage)
 {
-	return root->v.get_resources(root);
+	struct mail_storage *const *storages;
+	unsigned int i, count;
+	
+	storages = array_get(&quota->storages, &count);
+	for (i = 0; i < count; i++) {
+		if (storages[i] == storage) {
+			array_delete(&quota->storages, i, 1);
+			break;
+		}
+	}
 }
 
-int quota_get_resource(struct quota_root *root, const char *name,
-		       uint64_t *value_r, uint64_t *limit_r)
+struct quota_root_iter *
+quota_root_iter_init(struct quota *quota, struct mailbox *box)
 {
-	return root->v.get_resource(root, name, value_r, limit_r);
-}
+	struct quota_root_iter *iter;
 
-int quota_set_resource(struct quota_root *root,
-		       const char *name, uint64_t value)
-{
-	return root->v.set_resource(root, name, value);
+	iter = i_new(struct quota_root_iter, 1);
+	iter->quota = quota;
+	iter->box = box;
+	return iter;
 }
 
-struct quota_transaction_context *quota_transaction_begin(struct mailbox *box)
+struct quota_root *quota_root_iter_next(struct quota_root_iter *iter)
 {
-	struct quota_transaction_context *ctx;
-	struct quota_root_transaction_context *root_ctx;
-	struct quota_root_iter *iter;
-	struct quota_root *root;
+	struct quota_root *const *roots, *root = NULL;
+	unsigned int count;
+	uint64_t value, limit;
+	int ret;
 
-	ctx = i_new(struct quota_transaction_context, 1);
-	ARRAY_CREATE(&ctx->root_transactions, default_pool,
-		     struct quota_root_transaction_context *, 4);
+	roots = array_get(&iter->quota->roots, &count);
+	if (iter->i >= count)
+		return NULL;
 
-	iter = quota_root_iter_init(box);
-	while ((root = quota_root_iter_next(iter)) != NULL) {
-		root_ctx = root->v.transaction_begin(root, ctx);
-		array_append(&ctx->root_transactions, &root_ctx, 1);
+	for (; iter->i < count; iter->i++) {
+		ret = quota_get_resource(roots[iter->i], "",
+					 QUOTA_NAME_STORAGE, &value, &limit);
+		if (ret == 0) {
+			ret = quota_get_resource(roots[iter->i], "",
+						 QUOTA_NAME_MESSAGES,
+						 &value, &limit);
+		}
+		if (ret > 0) {
+			root = roots[iter->i];
+			break;
+		}
 	}
-	quota_root_iter_deinit(iter);
-	return ctx;
+
+	iter->i++;
+	return root;
 }
 
-static void quota_transaction_free(struct quota_transaction_context *ctx)
+void quota_root_iter_deinit(struct quota_root_iter *iter)
 {
-	array_free(&ctx->root_transactions);
-	i_free(ctx);
+	i_free(iter);
 }
 
-int quota_transaction_commit(struct quota_transaction_context *ctx)
+struct quota_root *quota_root_lookup(struct quota *quota, const char *name)
 {
-	struct quota_root_transaction_context *const *root_transactions;
+	struct quota_root *const *roots;
 	unsigned int i, count;
-	int ret = 0;
 
-	root_transactions = array_get(&ctx->root_transactions, &count);
+	roots = array_get(&quota->roots, &count);
 	for (i = 0; i < count; i++) {
-		struct quota_root_transaction_context *t =
-			root_transactions[i];
-
-		if (t->root->v.transaction_commit(t) < 0)
-			ret = -1;
+		if (strcmp(roots[i]->name, name) == 0)
+			return roots[i];
 	}
-
-	quota_transaction_free(ctx);
-	return ret;
+	return NULL;
 }
 
-void quota_transaction_rollback(struct quota_transaction_context *ctx)
+const char *quota_root_get_name(struct quota_root *root)
 {
-	struct quota_root_transaction_context *const *root_transactions;
-	unsigned int i, count;
-
-	root_transactions = array_get(&ctx->root_transactions, &count);
-	for (i = 0; i < count; i++) {
-		struct quota_root_transaction_context *t =
-			root_transactions[i];
-
-		t->root->v.transaction_rollback(t);
-	}
-
-	quota_transaction_free(ctx);
+	return root->name;
 }
 
-int quota_try_alloc(struct quota_transaction_context *ctx,
-		    struct mail *mail, bool *too_large_r)
+const char *const *quota_root_get_resources(struct quota_root *root)
 {
-	struct quota_root_transaction_context *const *root_transactions;
-	unsigned int i, count;
-	int ret = 1;
-
-	root_transactions = array_get(&ctx->root_transactions, &count);
-	for (i = 0; i < count; i++) {
-		struct quota_root_transaction_context *t =
-			root_transactions[i];
-
-		ret = t->root->v.try_alloc(t, mail, too_large_r);
-		if (ret <= 0)
-			break;
-	}
-	return ret;
+	return root->backend->v.get_resources(root);
 }
 
-int quota_try_alloc_bytes(struct quota_transaction_context *ctx,
-			  uoff_t size, bool *too_large_r)
+int quota_get_resource(struct quota_root *root, const char *mailbox_name,
+		       const char *name, uint64_t *value_r, uint64_t *limit_r)
 {
-	struct quota_root_transaction_context *const *root_transactions;
-	unsigned int i, count;
-	int ret = 1;
+	uint64_t bytes_limit, count_limit;
+	int ret;
 
-	root_transactions = array_get(&ctx->root_transactions, &count);
-	for (i = 0; i < count; i++) {
-		struct quota_root_transaction_context *t =
-			root_transactions[i];
+	(void)quota_root_get_rule_limits(root, mailbox_name,
+					 &bytes_limit, &count_limit);
+	if (strcmp(name, QUOTA_NAME_STORAGE) == 0)
+		*limit_r = bytes_limit;
+	else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0)
+		*limit_r = count_limit;
+	else
+		*limit_r = 0;
 
-		ret = t->root->v.try_alloc_bytes(t, size, too_large_r);
-		if (ret <= 0)
-			break;
-	}
-	return ret;
+	ret = root->backend->v.get_resource(root, name, value_r, limit_r);
+	return ret <= 0 ? ret :
+		(*limit_r == 0 ? 0 : 1);
 }
 
-int quota_test_alloc_bytes(struct quota_transaction_context *ctx,
-			   uoff_t size, bool *too_large_r)
+int quota_set_resource(struct quota_root *root __attr_unused__,
+		       const char *name __attr_unused__,
+		       uint64_t value __attr_unused__, const char **error_r)
 {
-	struct quota_root_transaction_context *const *root_transactions;
-	unsigned int i, count;
-	int ret = 1;
-
-	root_transactions = array_get(&ctx->root_transactions, &count);
-	for (i = 0; i < count; i++) {
-		struct quota_root_transaction_context *t =
-			root_transactions[i];
-
-		ret = t->root->v.test_alloc_bytes(t, size, too_large_r);
-		if (ret <= 0)
-			break;
-	}
-	return ret;
+	/* the quota information comes from userdb (or even config file),
+	   so there's really no way to support this until some major changes
+	   are done */
+	*error_r = MAIL_STORAGE_ERR_NO_PERMISSION;
+	return -1;
 }
 
-void quota_alloc(struct quota_transaction_context *ctx, struct mail *mail)
+struct quota_transaction_context *quota_transaction_begin(struct quota *quota,
+							  struct mailbox *box)
 {
-	struct quota_root_transaction_context *const *root_transactions;
+	struct quota_transaction_context *ctx;
+	struct quota_root *const *roots;
+	const char *mailbox_name;
 	unsigned int i, count;
+	uint64_t current, limit, left;
+	int ret;
 
-	root_transactions = array_get(&ctx->root_transactions, &count);
-	for (i = 0; i < count; i++) {
-		struct quota_root_transaction_context *t =
-			root_transactions[i];
+	mailbox_name = mailbox_get_name(box);
+	
+	ctx = i_new(struct quota_transaction_context, 1);
+	ctx->quota = quota;
+	ctx->box = box;
+	ctx->bytes_left = (uint64_t)-1;
+	ctx->count_left = (uint64_t) -1;
 
-		t->root->v.alloc(t, mail);
+	/* find the lowest quota limits from all roots and use them */
+	roots = array_get(&quota->roots, &count);
+	for (i = 0; i < count; i++) {
+		ret = quota_get_resource(roots[i], mailbox_name,
+					 QUOTA_NAME_STORAGE, &current, &limit);
+		if (ret > 0) {
+			left = limit < current ? 0 : limit - current;
+			if (ctx->bytes_left > left)
+				ctx->bytes_left = left;
+		} else if (ret < 0) {
+			ctx->failed = TRUE;
+			break;
+		}
+		
+		ret = quota_get_resource(roots[i], mailbox_name,
+					 QUOTA_NAME_MESSAGES, &current, &limit);
+		if (ret > 0) {
+			left = limit < current ? 0 : limit - current;
+			if (ctx->count_left > left)
+				ctx->count_left = left;
+		} else if (ret < 0) {
+			ctx->failed = TRUE;
+			break;
+		}
 	}
+	return ctx;
 }
 
-void quota_free(struct quota_transaction_context *ctx, struct mail *mail)
+int quota_transaction_commit(struct quota_transaction_context *ctx)
 {
-	struct quota_root_transaction_context *const *root_transactions;
+	struct quota_root *const *roots;
 	unsigned int i, count;
+	int ret = 0;
 
-	root_transactions = array_get(&ctx->root_transactions, &count);
-	for (i = 0; i < count; i++) {
-		struct quota_root_transaction_context *t =
-			root_transactions[i];
-
-		t->root->v.free(t, mail);
+	if (ctx->failed)
+		ret = -1;
+	else {
+		roots = array_get(&ctx->quota->roots, &count);
+		for (i = 0; i < count; i++) {
+			if (roots[i]->backend->v.update(roots[i], ctx) < 0)
+				ret = -1;
+		}
 	}
-}
 
-const char *quota_last_error(struct quota *quota)
-{
-	return quota->last_error != NULL ? quota->last_error : "Unknown error";
-}
-
-void quota_set_error(struct quota *quota, const char *errormsg)
-{
-	i_free(quota->last_error);
-	quota->last_error = i_strdup(errormsg);
+	i_free(ctx);
+	return ret;
 }
 
-void
-quota_default_transaction_rollback(struct quota_root_transaction_context *ctx)
+void quota_transaction_rollback(struct quota_transaction_context *ctx)
 {
 	i_free(ctx);
 }
 
-int quota_default_try_alloc_bytes(struct quota_root_transaction_context *ctx,
-				  uoff_t size, bool *too_large_r)
+int quota_try_alloc(struct quota_transaction_context *ctx,
+		    struct mail *mail, bool *too_large_r)
 {
 	int ret;
 
-	ret = quota_default_test_alloc_bytes(ctx, size, too_large_r);
-	if (ret <= 0 || ctx->disabled)
+	ret = quota_test_alloc(ctx, mail_get_physical_size(mail), too_large_r);
+	if (ret <= 0)
 		return ret;
 
-	ctx->count_diff++;
-	ctx->bytes_diff += size;
+	quota_alloc(ctx, mail);
 	return 1;
 }
 
-int quota_default_test_alloc_bytes(struct quota_root_transaction_context *ctx,
-				   uoff_t size, bool *too_large_r)
+int quota_test_alloc(struct quota_transaction_context *ctx,
+		     uoff_t size, bool *too_large_r)
 {
-	if (ctx->disabled) {
-		*too_large_r = FALSE;
-		return 1;
-	}
-	if (ctx->bytes_current == (uint64_t)-1) {
-		/* failure in transaction initialization */
-		return -1;
-	}
+	struct quota_root *const *roots;
+	unsigned int i, count;
 
-	*too_large_r = size > ctx->bytes_limit;
+	*too_large_r = FALSE;
 
-	if (ctx->bytes_current + ctx->bytes_diff + size > ctx->bytes_limit)
-		return 0;
-	if (ctx->count_current + ctx->count_diff + 1 > ctx->count_limit)
-		return 0;
-	return 1;
-}
+	if (ctx->failed)
+		return -1;
+	if (ctx->count_left != 0 && ctx->bytes_left >= ctx->bytes_used + size)
+		return 1;
 
-int quota_default_try_alloc(struct quota_root_transaction_context *ctx,
-			    struct mail *mail, bool *too_large_r)
-{
-	uoff_t size;
+	roots = array_get(&ctx->quota->roots, &count);
+	for (i = 0; i < count; i++) {
+		uint64_t bytes_limit, count_limit;
 
-	if (ctx->disabled)
-		return 1;
+		if (!quota_root_get_rule_limits(roots[i],
+						mailbox_get_name(ctx->box),
+						&bytes_limit, &count_limit))
+			continue;
 
-	size = mail_get_physical_size(mail);
-	if (size == (uoff_t)-1) {
-		mail_storage_set_critical(mail->box->storage,
-			"Quota: Couldn't get new message's size");
-		return -1;
+		/* if size is bigger than any limit, then
+		   it is bigger than the lowest limit */
+		if (size > bytes_limit) {
+			*too_large_r = TRUE;
+			break;
+		}
 	}
 
-	return quota_default_try_alloc_bytes(ctx, size, too_large_r);
+	return 0;
 }
 
-void quota_default_alloc(struct quota_root_transaction_context *ctx,
-			 struct mail *mail)
+void quota_alloc(struct quota_transaction_context *ctx, struct mail *mail)
 {
 	uoff_t size;
 
-	if (ctx->disabled)
-		return;
-
 	size = mail_get_physical_size(mail);
 	if (size != (uoff_t)-1)
-		ctx->bytes_diff += size;
-	ctx->count_diff++;
+		ctx->bytes_used += size;
+
+	ctx->count_used++;
 }
 
-void quota_default_free(struct quota_root_transaction_context *ctx,
-			struct mail *mail)
+void quota_free(struct quota_transaction_context *ctx, struct mail *mail)
 {
 	uoff_t size;
 
-	if (ctx->disabled)
-		return;
-
 	size = mail_get_physical_size(mail);
 	if (size != (uoff_t)-1)
-		ctx->bytes_diff -= size;
-	ctx->count_diff--;
+		ctx->bytes_used -= size;
+
+	ctx->count_used--;
 }

Index: quota.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/plugins/quota/quota.h,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- quota.h	11 Jun 2006 17:39:04 -0000	1.5
+++ quota.h	30 Jul 2006 17:58:43 -0000	1.6
@@ -19,20 +19,17 @@
 struct quota *quota_init(void);
 void quota_deinit(struct quota *quota);
 
-/* Create a new quota setup under which quota roots are created.
-   user_root is TRUE if this quota points to user's own mailboxes instead of
-   shared mailboxes. */
-struct quota_setup *
-quota_setup_init(struct quota *quota, const char *data, bool user_root);
-void quota_setup_deinit(struct quota_setup *setup);
-
 /* Create a new quota root. */
-struct quota_root *
-quota_root_init(struct quota_setup *setup, const char *name);
+struct quota_root *quota_root_init(struct quota *quota, const char *root_def);
 void quota_root_deinit(struct quota_root *root);
 
+/* Add a new rule too the quota root. Returns 0 if ok, -1 if rule is invalid. */
+int quota_root_add_rule(struct quota_root *root, const char *rule_def,
+			const char **error_r);
+
 /* List all quota roots. Returned quota roots are freed by quota_deinit(). */
-struct quota_root_iter *quota_root_iter_init(struct mailbox *box);
+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);
 void quota_root_iter_deinit(struct quota_root_iter *iter);
 
@@ -45,14 +42,15 @@
 const char *const *quota_root_get_resources(struct quota_root *root);
 
 /* Returns 1 if quota value was found, 0 if not, -1 if error. */
-int quota_get_resource(struct quota_root *root,
+int quota_get_resource(struct quota_root *root, const char *mailbox_name,
 		       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);
+int quota_set_resource(struct quota_root *root, const char *name,
+		       uint64_t value, const char **error_r);
 
 /* Start a new quota transaction. */
-struct quota_transaction_context *quota_transaction_begin(struct mailbox *box);
+struct quota_transaction_context *quota_transaction_begin(struct quota *quota, 
+							  struct mailbox *box);
 /* Commit quota transaction. Returns 0 if ok, -1 if failed. */
 int quota_transaction_commit(struct quota_transaction_context *ctx);
 /* Rollback quota transaction changes. */
@@ -63,16 +61,11 @@
    too_large_r is set to TRUE. */
 int quota_try_alloc(struct quota_transaction_context *ctx,
 		    struct mail *mail, bool *too_large_r);
-int quota_try_alloc_bytes(struct quota_transaction_context *ctx,
-			  uoff_t size, bool *too_large_r);
-/* Like quota_try_alloc_bytes(), but don't actually update the quota. */
-int quota_test_alloc_bytes(struct quota_transaction_context *ctx,
-			   uoff_t size, bool *too_large_r);
+/* Like quota_try_alloc(), but don't actually allocate anything. */
+int quota_test_alloc(struct quota_transaction_context *ctx,
+		     uoff_t size, bool *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