quota.c revision 968aab2a00d1b53a53854fc2e97d1c632171a54e
/* Copyright (c) 2005-2010 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "hash.h"
#include "str.h"
#include "network.h"
#include "write-full.h"
#include "eacces-error.h"
#include "mailbox-list-private.h"
#include "quota-private.h"
#include "quota-fs.h"
#include <ctype.h>
#include <stdlib.h>
#define DEFAULT_QUOTA_EXCEEDED_MSG \
"Quota exceeded (mailbox for user is full)"
#define RULE_NAME_DEFAULT_FORCE "*"
#define RULE_NAME_DEFAULT_NONFORCE "?"
struct quota_root_iter {
unsigned int i;
};
unsigned int quota_module_id = 0;
extern struct quota_backend quota_backend_dict;
extern struct quota_backend quota_backend_dirsize;
extern struct quota_backend quota_backend_fs;
extern struct quota_backend quota_backend_maildir;
static const struct quota_backend *quota_backends[] = {
#ifdef HAVE_FS_QUOTA
#endif
};
{
unsigned int i;
for (i = 0; i < N_ELEMENTS(quota_backends); i++) {
return quota_backends[i];
}
return NULL;
}
struct quota_root_settings *root_set,
const char **error_r)
{
unsigned int i;
for (i = 2;; i++) {
break;
return -1;
}
}
return 0;
}
static int
struct quota_root_settings *root_set,
const char **error_r)
{
unsigned int i;
for (i = 2;; i++) {
break;
rule);
return -1;
}
}
return 0;
}
static int
struct quota_root_settings **set_r,
const char **error_r)
{
struct quota_root_settings *root_set;
const struct quota_backend *backend;
const char *p, *args, *backend_name;
/* <backend>[:<quota root name>[:<backend args>]] */
if (p == NULL) {
} else {
args = p + 1;
}
return -1;
}
/* save root's name */
if (p == NULL) {
} else {
args = p + 1;
}
} else {
}
i_debug("Quota root: name=%s backend=%s args=%s",
}
return 0;
}
static int
{
struct quota_root_settings *root_set;
return -1;
return -1;
return -1;
return 0;
}
struct quota_settings **set_r,
const char **error_r)
{
struct quota_settings *quota_set;
unsigned int i;
for (i = 2;; i++) {
break;
&error) < 0) {
pool_unref(&pool);
return -1;
}
}
pool_unref(&pool);
return 0;
}
return 1;
}
{
*_quota_set = NULL;
}
{
pool_unref(&pool);
}
static int
{
struct quota_root *root;
const char *const *tmp;
sizeof(void *), 10);
*error_r = "init() failed";
return -1;
}
else
break;
}
"Unknown parameter for backend %s: %s",
return -1;
}
}
return 0;
}
return 1;
}
{
struct quota_root *root;
struct quota_root_settings *const *root_sets;
unsigned int i, count;
const char *error;
int ret;
for (i = 0; i < count; i++) {
if (ret < 0) {
return -1;
}
if (ret > 0)
}
return 0;
}
{
struct quota_root *const *roots;
unsigned int i, count;
for (i = 0; i < count; i++)
quota_root_deinit(roots[i]);
/* deinit quota roots before setting quser->quota=NULL */
}
struct quota_rule *
{
struct quota_rule *rule;
return rule;
}
return NULL;
}
static int
struct quota_rule *rule,
{
"Invalid rule percentage: %lld", (long long)percentage);
return -1;
}
*error_r = "Default rule can't be a percentage";
return -1;
}
else
i_unreached();
return 0;
}
static void
{
if (rule->bytes_percent > 0)
if (rule->count_percent > 0)
}
{
struct quota_rule *rule;
struct quota_warning_rule *warning_rule;
}
}
}
static int
const char *full_rule_def,
bool relative_rule, const char **error_r)
{
char *p;
multiply = 1;
value = "";
else
if (*value == '+') {
if (!relative_rule) {
*error_r = "Rule limit cannot have '+'";
return -1;
}
value++;
i_warning("quota root %s rule %s: "
"obsolete configuration for rule '%s' "
"should be changed to '%s=+%s'",
}
multiply = 1024;
} else {
"Unknown rule limit name: %s", key);
return -1;
}
switch (i_toupper(*p)) {
case '\0':
/* default */
break;
case 'B':
multiply = 1;
break;
case 'K':
multiply = 1024;
break;
case 'M':
break;
case 'G':
break;
case 'T':
break;
case '%':
multiply = 0;
error_r) < 0)
return -1;
break;
default:
"Invalid rule limit value: %s", *args);
return -1;
}
}
if (!relative_rule) {
if (rule->bytes_limit < 0) {
*error_r = "Bytes limit can't be negative";
return -1;
}
if (rule->count_limit < 0) {
*error_r = "Count limit can't be negative";
return -1;
}
}
return 0;
}
{
struct quota_rule *rule;
const char *p, *mailbox_name;
int ret = 0;
if (p == NULL) {
*error_r = "Invalid rule";
return -1;
}
/* <mailbox name>:<quota limits> */
} else {
rule->mailbox_name =
}
}
if (strcmp(p, "ignore") == 0) {
i_debug("Quota rule: root=%s mailbox=%s ignored",
}
return 0;
}
*error_r = "backend rule not supported";
ret = -1;
p + 8, error_r))
ret = -1;
} else {
relative_rule, error_r) < 0)
ret = -1;
}
const char *rule_plus =
i_debug("Quota rule: root=%s mailbox=%s "
"bytes=%s%lld%s messages=%s%lld%s",
(long long)rule->bytes_limit,
(long long)rule->count_limit,
}
return ret;
}
const char *mailbox_name,
{
struct quota_rule *rule;
bool enabled;
return -1;
}
}
/* if default rule limits are 0, user has unlimited quota.
ignore any specific quota rules */
} else {
bytes_limit = 0;
count_limit = 0;
}
}
return enabled ? 1 : 0;
}
{
struct quota_root *const *roots;
struct mail_namespace *const *namespaces;
struct quota_backend **backends;
unsigned int i, j, count;
/* first check if there already exists a namespace with the exact same
path. we don't want to count them twice. */
for (i = 0; i < count; i++) {
/* duplicate */
return;
}
}
}
/* @UNSAFE: get different backends into one array */
for (i = 0; i < count; i++) {
break;
}
}
}
}
{
struct mail_namespace *const *namespaces;
unsigned int i, count;
/* no quota for this namespace */
return;
}
for (i = 0; i < count; i++) {
if (namespaces[i] == ns) {
break;
}
}
}
{
struct quota_warning_rule *warning;
struct quota_rule rule;
const char *p, *q;
int ret;
*error_r = "No command specified";
return -1;
}
if (*rule_def == '+') {
/* warn when exceeding quota */
q = rule_def+1;
} else if (*rule_def == '-') {
/* warn when going below quota */
q = rule_def+1;
} else {
/* default: same as '+' */
q = rule_def;
}
if (ret < 0)
return -1;
i_debug("Quota warning: bytes=%llu%s "
"messages=%llu%s reverse=%s command=%s",
}
return 0;
}
struct quota_root_iter *
{
struct quota_root_iter *iter;
return iter;
}
struct mail_namespace *ns)
{
struct mail_storage *storage;
const char *name = "";
/* this check works as long as there is only one storage per list */
return FALSE;
return FALSE;
} else {
return FALSE;
}
return TRUE;
}
static bool
bool enforce)
{
/* we don't want to include this root in quota enforcing */
return FALSE;
}
return FALSE;
/* a single quota root: don't bother checking further */
return TRUE;
}
}
{
unsigned int count;
int ret;
return NULL;
continue;
if (ret == -1) {
}
if (ret == 0) {
}
if (ret > 0) {
break;
}
}
iter->i++;
return root;
}
{
}
{
struct quota_root *const *roots;
unsigned int i, count;
for (i = 0; i < count; i++) {
return roots[i];
}
return NULL;
}
{
}
{
}
{
int ret;
}
/* Get the value first. This call may also update quota limits if
they're defined externally. */
if (ret <= 0)
return ret;
&bytes_limit, &count_limit) < 0)
return -1;
*limit_r = bytes_limit;
*limit_r = count_limit;
else
*limit_r = 0;
if (kilobytes) {
*value_r /= 1024;
*limit_r /= 1024;
}
return *limit_r == 0 ? 0 : 1;
}
const char *name ATTR_UNUSED,
{
/* 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 */
return -1;
}
{
struct quota_transaction_context *ctx;
return ctx;
}
{
struct quota_root *const *roots;
const char *mailbox_name;
unsigned int i, count;
int ret;
/* find the lowest quota limits from all roots and use them */
for (i = 0; i < count; i++) {
continue;
&count_limit) < 0) {
return -1;
}
if (bytes_limit > 0) {
if (ret > 0) {
} else if (ret < 0) {
return -1;
}
}
if (count_limit > 0) {
if (ret > 0) {
} else if (ret < 0) {
return -1;
}
}
}
return 0;
}
{
const char *socket_path, *const *args;
int fd;
socket_path = args[0];
args++;
if (*socket_path != '/') {
socket_path, NULL);
}
i_error("quota: %s",
eacces_error_get("net_connect_unix",
socket_path));
} else {
i_error("quota: net_connect_unix(%s) failed: %m",
}
return;
}
}
}
static bool
quota_warning_match(const struct quota_warning_rule *w,
{
if (!w->reverse) {
/* over quota (default) */
} else {
}
}
struct quota_root *root)
{
struct quota_warning_rule *warnings;
unsigned int i, count;
if (count == 0)
return;
&bytes_current, &bytes_limit) < 0)
return;
&count_current, &count_limit) < 0)
return;
for (i = 0; i < count; i++) {
if (quota_warning_match(&warnings[i],
break;
}
}
}
{
struct quota_rule *rule;
struct quota_root *const *roots;
unsigned int i, count;
const char *mailbox_name;
int ret = 0;
ret = -1;
for (i = 0; i < count; i++) {
continue;
/* mailbox not included in quota */
continue;
}
ret = -1;
else
}
/* execute quota warnings after all updates. this makes it
work correctly regardless of whether backend.get_resource()
returns updated values before backend.update() or not */
} T_END;
return ret;
}
{
}
{
int ret;
return -1;
if (ret <= 0)
return ret;
return 1;
}
{
return -1;
if (!ctx->limits_set) {
if (quota_transaction_set_limits(ctx) < 0)
return -1;
}
}
{
struct quota_root *const *roots;
unsigned int i, count;
int ret;
*too_large_r = FALSE;
return 1;
for (i = 0; i < count; i++) {
continue;
&bytes_limit, &count_limit);
if (ret == 0)
continue;
if (ret < 0)
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 0;
}
{
ctx->count_used++;
}
{
else
}
{
ctx->count_used--;
}
{
}