#include <config.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include "assert.h"
#include "cyrusdb.h"
#include "exitcodes.h"
#include "global.h"
#include "imap_err.h"
#include "mailbox.h"
#include "quota.h"
#include "util.h"
#include "xmalloc.h"
#define QDB config_quota_db
struct db *qdb;
static int quota_dbopen = 0;
int quota_read(struct quota *quota, struct txn **tid, int wrlock)
{
int r;
int qrlen;
const char *data;
int datalen;
if (!quota->root || !(qrlen = strlen(quota->root)))
return IMAP_QUOTAROOT_NONEXISTENT;
if (wrlock)
r = QDB->fetchlock(qdb, quota->root, qrlen, &data, &datalen, tid);
else
r = QDB->fetch(qdb, quota->root, qrlen, &data, &datalen, tid);
switch (r) {
case CYRUSDB_OK:
sscanf(data, "%lu %d", "a->used, "a->limit);
break;
case CYRUSDB_AGAIN:
return IMAP_AGAIN;
break;
case CYRUSDB_NOTFOUND:
return IMAP_QUOTAROOT_NONEXISTENT;
break;
default:
syslog(LOG_ERR, "DBERROR: error fetching %s: %s",
quota->root, cyrusdb_strerror(r));
return IMAP_IOERROR;
break;
}
return 0;
}
void quota_commit(struct txn **tid)
{
if (tid && *tid) {
if (QDB->commit(qdb, *tid)) {
syslog(LOG_ERR, "IOERROR: committing quota: %m");
}
*tid = NULL;
}
}
void quota_abort(struct txn **tid)
{
if (tid && *tid) {
if (QDB->abort(qdb, *tid)) {
syslog(LOG_ERR, "IOERROR: aborting quota: %m");
}
*tid = NULL;
}
}
int quota_write(struct quota *quota, struct txn **tid)
{
int r;
int qrlen, len;
char buf[1024];
if (!quota->root) return 0;
qrlen = strlen(quota->root);
if (!qrlen) return IMAP_QUOTAROOT_NONEXISTENT;
len = snprintf(buf, sizeof(buf) - 1,
"%lu %d", quota->used, quota->limit);
r = QDB->store(qdb, quota->root, qrlen, buf, len, tid);
switch (r) {
case CYRUSDB_OK:
break;
case CYRUSDB_AGAIN:
return IMAP_AGAIN;
break;
default:
syslog(LOG_ERR, "DBERROR: error storing %s: %s",
quota->root, cyrusdb_strerror(r));
return IMAP_IOERROR;
break;
}
return 0;
}
int quota_delete(struct quota *quota, struct txn **tid)
{
int qrlen, r;
if (!quota->root) return 0;
qrlen = strlen(quota->root);
if (!qrlen) return IMAP_QUOTAROOT_NONEXISTENT;
r = QDB->delete(qdb, quota->root, qrlen, tid, 0);
return r;
}
int quota_findroot(char *ret, size_t retlen, const char *name)
{
char *tail, *p, *mbox;
strlcpy(ret, name, retlen);
mbox = (config_virtdomains && (p = strchr(ret, '!'))) ? p+1 : ret;
tail = mbox + strlen(mbox);
while (QDB->fetch(qdb, ret, strlen(ret), NULL, NULL, NULL)) {
tail = strrchr(mbox, '.');
if (!tail) break;
*tail = '\0';
}
if (tail) return 1;
if (mbox == ret) return 0;
*mbox = '\0';
return (QDB->fetch(qdb, ret, strlen(ret), NULL, NULL, NULL) == 0);
}
void quotadb_init(int myflags)
{
int r;
if (myflags & QUOTADB_SYNC) {
r = QDB->sync();
}
}
void quotadb_open(char *fname)
{
int ret;
char *tofree = NULL;
if (!fname) {
size_t fname_len = strlen(config_dir)+strlen(FNAME_QUOTADB)+1;
fname = xmalloc(fname_len);
tofree = fname;
strlcpy(fname, config_dir, fname_len);
strlcat(fname, FNAME_QUOTADB, fname_len);
}
ret = QDB->open(fname, CYRUSDB_CREATE, &qdb);
if (ret != 0) {
syslog(LOG_ERR, "DBERROR: opening %s: %s", fname,
cyrusdb_strerror(ret));
fatal("can't read quotas file", EC_TEMPFAIL);
}
if (tofree) free(tofree);
quota_dbopen = 1;
}
void quotadb_close(void)
{
int r;
if (quota_dbopen) {
r = QDB->close(qdb);
if (r) {
syslog(LOG_ERR, "DBERROR: error closing quotas: %s",
cyrusdb_strerror(r));
}
quota_dbopen = 0;
}
}
void quotadb_done(void)
{
}