[dovecot-cvs] dovecot/src/plugins/expire .cvsignore, NONE, 1.1 Makefile.am, NONE, 1.1 auth-client.c, NONE, 1.1 auth-client.h, NONE, 1.1 expire-env.c, NONE, 1.1 expire-env.h, NONE, 1.1 expire-plugin.c, NONE, 1.1 expire-plugin.h, NONE, 1.1 expire-tool.c, NONE, 1.1
tss-movial at dovecot.org
tss-movial at dovecot.org
Mon Jul 31 02:12:56 EEST 2006
Update of /var/lib/cvs/dovecot/src/plugins/expire
In directory talvi:/tmp/cvs-serv31018/src/plugins/expire
Added Files:
.cvsignore Makefile.am auth-client.c auth-client.h
expire-env.c expire-env.h expire-plugin.c expire-plugin.h
expire-tool.c
Log Message:
Initial import of expire plugin code. Seems to work with at least one user. :)
--- NEW FILE: .cvsignore ---
*.la
*.lo
*.o
.deps
.libs
Makefile
Makefile.in
so_locations
expire-tool
--- 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-index \
-I$(top_srcdir)/src/lib-storage \
-I$(top_srcdir)/src/lib-storage/index \
-DPKG_RUNDIR=\""$(rundir)"\"
lib01_expire_plugin_la_LDFLAGS = -module -avoid-version
module_LTLIBRARIES = \
lib01_expire_plugin.la
lib01_expire_plugin_la_SOURCES = \
expire-env.c \
expire-plugin.c
noinst_HEADERS = \
auth-client.h \
expire-env.h \
expire-plugin.h
noinst_PROGRAMS = expire-tool
expire_tool_SOURCES = \
auth-client.c \
expire-tool.c
libs = \
$(top_builddir)/src/lib-storage/register/libstorage-register.a \
$(STORAGE_LIBS) \
$(top_builddir)/src/lib-storage/libstorage.a \
$(top_builddir)/src/lib-storage/subscription-file/libstorage_subscription_file.a \
$(top_builddir)/src/lib-imap/libimap.a \
$(top_builddir)/src/lib-mail/libmail.a \
$(top_builddir)/src/lib-dict/libdict.a \
$(top_builddir)/src/lib-charset/libcharset.a \
$(top_builddir)/src/lib/liblib.a
expire_tool_LDADD = \
$(libs) \
$(LIBICONV) \
$(RAND_LIBS)
expire_tool_DEPENDENCIES = $(libs)
install-exec-local:
$(mkdir_p) $(DESTDIR)$(moduledir)/imap \
$(DESTDIR)$(moduledir)/pop3 \
$(DESTDIR)$(moduledir)/lda
for d in imap pop3 lda; do \
rm -f $(DESTDIR)$(moduledir)/$$d/lib01_expire_plugin.so; \
$(LN_S) ../lib01_expire_plugin.so $(DESTDIR)$(moduledir)/$$d; \
done
--- NEW FILE: auth-client.c ---
/* Copyright (C) 2005-2006 Timo Sirainen */
#include "lib.h"
#include "ioloop.h"
#include "network.h"
#include "istream.h"
#include "ostream.h"
#include "env-util.h"
#include "restrict-access.h"
#include "auth-client.h"
#include <stdlib.h>
#include <unistd.h>
#define MAX_INBUF_SIZE 8192
#define MAX_OUTBUF_SIZE 512
struct auth_connection {
char *auth_socket;
int fd;
struct io *io;
struct istream *input;
struct ostream *output;
uid_t orig_uid, current_uid;
const char *current_user;
int return_value;
unsigned int handshaked:1;
};
static void auth_input(void *context);
static int auth_connection_connect(struct auth_connection *conn)
{
int fd;
if (conn->fd != -1)
return 0;
fd = net_connect_unix(conn->auth_socket);
if (fd < 0) {
i_error("net_connect(%s) failed: %m", conn->auth_socket);
return -1;
}
conn->fd = fd;
conn->input =
i_stream_create_file(fd, default_pool, MAX_INBUF_SIZE, FALSE);
conn->output =
o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE, FALSE);
conn->io = io_add(fd, IO_READ, auth_input, conn);
o_stream_send_str(conn->output, "VERSION\t1\t0\n");
return 0;
}
static void auth_connection_close(struct auth_connection *conn)
{
if (conn->fd == -1)
return;
io_remove(&conn->io);
i_stream_unref(&conn->input);
o_stream_unref(&conn->output);
if (close(conn->fd) < 0)
i_error("close() failed: %m");
conn->fd = -1;
}
struct auth_connection *auth_connection_init(const char *auth_socket)
{
struct auth_connection *conn;
conn = i_new(struct auth_connection, 1);
conn->auth_socket = i_strdup(auth_socket);
conn->orig_uid = conn->current_uid = geteuid();
conn->fd = -1;
(void)auth_connection_connect(conn);
return conn;
}
void auth_connection_deinit(struct auth_connection *conn)
{
auth_connection_close(conn);
i_free(conn->auth_socket);
i_free(conn);
}
static void auth_parse_input(struct auth_connection *conn, const char *args)
{
const char *const *tmp, *key, *value;
uid_t uid = (uid_t)-1;
int home_found = FALSE;
for (tmp = t_strsplit(args, "\t"); *tmp != NULL; tmp++) {
if (strncmp(*tmp, "uid=", 4) == 0)
uid = strtoul(*tmp + 4, NULL, 10);
else if (strncmp(*tmp, "gid=", 4) == 0) {
gid_t gid = strtoul(*tmp + 4, NULL, 10);
if (conn->orig_uid == 0 || getegid() != gid) {
env_put(t_strconcat("RESTRICT_SETGID=",
*tmp + 4, NULL));
}
} else if (strncmp(*tmp, "chroot=", 7) == 0) {
env_put(t_strconcat("RESTRICT_CHROOT=",
*tmp + 7, NULL));
} else if (strncmp(*tmp, "home=", 5) == 0) {
home_found = TRUE;
env_put(t_strconcat("HOME=", *tmp + 5, NULL));
} else {
key = t_str_ucase(t_strcut(*tmp, '='));
value = strchr(*tmp, '=');
if (value != NULL)
env_put(t_strconcat(key, "=", value+1, NULL));
}
}
if (!home_found) {
/* we must have a home directory */
i_error("userdb(%s) didn't return a home directory",
conn->current_user);
return;
}
if (uid == (uid_t)-1) {
i_error("userdb(%s) didn't return uid", conn->current_user);
return;
}
/* we'll change only effective UID. This is a bit unfortunate since
it allows reverting back to root, but we'll have to be able to
access different users' mailboxes.. */
if (uid != conn->current_uid) {
if (conn->current_uid != 0) {
if (seteuid(0) != 0)
i_fatal("seteuid(0) failed: %m");
}
if (seteuid(uid) < 0)
i_fatal("seteuid(%s) failed: %m", dec2str(uid));
conn->current_uid = uid;
}
restrict_access_by_env(FALSE);
conn->return_value = 1;
}
static void auth_input(void *context)
{
struct auth_connection *conn = context;
const char *line;
switch (i_stream_read(conn->input)) {
case 0:
return;
case -1:
/* disconnected */
auth_connection_close(conn);
return;
case -2:
/* buffer full */
i_error("BUG: Auth master sent us more than %d bytes",
MAX_INBUF_SIZE);
auth_connection_close(conn);
return;
}
if (!conn->handshaked) {
while ((line = i_stream_next_line(conn->input)) != NULL) {
if (strncmp(line, "VERSION\t", 8) == 0) {
if (strncmp(line + 8, "1\t", 2) != 0) {
i_error("Auth master version mismatch");
auth_connection_close(conn);
return;
}
} else if (strncmp(line, "SPID\t", 5) == 0) {
conn->handshaked = TRUE;
break;
}
}
}
line = i_stream_next_line(conn->input);
if (line != NULL) {
if (strncmp(line, "USER\t1\t", 7) == 0) {
auth_parse_input(conn, line + 7);
} else if (strcmp(line, "NOTFOUND\t1") == 0)
conn->return_value = 0;
else if (strncmp(line, "FAIL\t1\t", 7) == 0)
conn->return_value = -1;
else {
i_error("BUG: Unexpected input from auth master: %s",
line);
auth_connection_close(conn);
}
io_loop_stop(current_ioloop);
}
}
int auth_client_put_user_env(struct auth_connection *conn,
const char *user)
{
if (auth_connection_connect(conn) < 0)
return -1;
conn->current_user = user;
conn->return_value = -1;
o_stream_send_str(conn->output,
t_strconcat("USER\t1\t", user, "\t"
"service=expire\n", NULL));
io_loop_run(current_ioloop);
conn->current_user = NULL;
return conn->return_value;
}
--- NEW FILE: auth-client.h ---
#ifndef __AUTH_CLIENT_H
#define __AUTH_CLIENT_H
struct auth_connection *auth_connection_init(const char *auth_socket);
void auth_connection_deinit(struct auth_connection *conn);
/* Returns -1 = error, 0 = user not found, 1 = ok */
int auth_client_put_user_env(struct auth_connection *conn,
const char *user);
#endif
--- NEW FILE: expire-env.c ---
/* Copyright (C) 2006 PT.COM / SAPO. Code by Timo Sirainen. */
#include "lib.h"
#include "array.h"
#include "expire-env.h"
#include <stdlib.h>
struct expire_env {
pool_t pool;
ARRAY_DEFINE(expire_boxes, struct expire_box);
};
struct expire_env *expire_env_init(const char *str)
{
struct expire_env *env;
struct expire_box box;
pool_t pool;
char *const *names;
unsigned int len;
pool = pool_alloconly_create("Expire pool", 512);
env = p_new(pool, struct expire_env, 1);
env->pool = pool;
names = p_strsplit(pool, str, " ");
len = strarray_length((const char *const *)names);
ARRAY_CREATE(&env->expire_boxes, pool, struct expire_box, len / 2);
for (; *names != NULL; names += 2) {
if (names[1] == NULL) {
i_fatal("expire: Missing expire days for mailbox '%s'",
*names);
}
box.name = *names;
box.expire_secs = strtoul(names[1], NULL, 10) * 3600 * 24;
array_append(&env->expire_boxes, &box, 1);
}
return env;
}
void expire_env_deinit(struct expire_env *env)
{
pool_unref(env->pool);
}
const struct expire_box *expire_box_find(struct expire_env *env,
const char *name)
{
const struct expire_box *expire_boxes;
unsigned int i, count;
expire_boxes = array_get(&env->expire_boxes, &count);
for (i = 0; i < count; i++) {
if (strcmp(name, expire_boxes[i].name) == 0)
return &expire_boxes[i];
}
return NULL;
}
--- NEW FILE: expire-env.h ---
#ifndef __EXPIRE_ENV_H
#define __EXPIRE_ENV_H
struct expire_env;
struct expire_box {
const char *name;
time_t expire_secs;
};
struct expire_env *expire_env_init(const char *str);
void expire_env_deinit(struct expire_env *env);
const struct expire_box *expire_box_find(struct expire_env *env,
const char *name);
#endif
--- NEW FILE: expire-plugin.c ---
/* Copyright (C) 2006 PT.COM / SAPO. Code by Tianyan Liu */
#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "dict.h"
#include "index-mail.h"
#include "index-storage.h"
#include "expire-env.h"
#include "expire-plugin.h"
#include <stdlib.h>
#define EXPIRE_CONTEXT(obj) \
*((void **)array_idx_modifiable(&(obj)->module_contexts, \
expire.storage_module_id))
struct expire {
struct dict *db;
struct expire_env *env;
const char *username;
unsigned int storage_module_id;
bool storage_module_id_set;
void (*next_hook_mail_storage_created)(struct mail_storage *storage);
};
struct expire_mail_storage {
struct mail_storage_vfuncs super;
};
struct expire_mailbox {
struct mailbox_vfuncs super;
time_t expire_secs;
};
struct expire_mail {
struct mail_vfuncs super;
};
struct expire_transaction_context {
struct mail *mail;
time_t first_save_time;
unsigned int first_expunged:1;
};
/* defined by imap, pop3, lda */
extern void (*hook_mail_storage_created)(struct mail_storage *storage);
static struct expire expire;
static struct mailbox_transaction_context *
expire_mailbox_transaction_begin(struct mailbox *box,
enum mailbox_transaction_flags flags)
{
struct expire_mailbox *xpr_box = EXPIRE_CONTEXT(box);
struct mailbox_transaction_context *t;
struct expire_transaction_context *xt;
t = xpr_box->super.transaction_begin(box, flags);
xt = i_new(struct expire_transaction_context, 1);
xt->mail = mail_alloc(t, 0, NULL);
array_idx_set(&t->module_contexts, expire.storage_module_id, &xt);
return t;
}
static int first_nonexpunged_timestamp(struct mailbox_transaction_context *_t,
time_t *stamp_r)
{
struct index_transaction_context *t =
(struct index_transaction_context *)_t;
struct expire_transaction_context *xt = EXPIRE_CONTEXT(_t);
struct mail_index_view *view = t->trans_view;
const struct mail_index_header *hdr;
const struct mail_index_record *rec;
uint32_t seq;
int ret = 0;
/* find the first non-expunged mail. we're here because the first
mail was expunged, so don't bother checking it. */
hdr = mail_index_get_header(view);
for (seq = 2; seq <= hdr->messages_count; seq++) {
ret = mail_index_lookup(view, seq, &rec);
if (ret != 0)
break;
}
if (ret < 0) {
*stamp_r = 0;
return -1;
}
if (ret > 0) {
mail_set_seq(xt->mail, seq);
*stamp_r = mail_get_save_date(xt->mail);
if (*stamp_r == (time_t)-1)
return -1;
} else {
/* everything expunged */
*stamp_r = 0;
}
return 0;
}
static int
expire_mailbox_transaction_commit(struct mailbox_transaction_context *t,
enum mailbox_sync_flags flags)
{
struct expire_mailbox *xpr_box = EXPIRE_CONTEXT(t->box);
struct expire_transaction_context *xt = EXPIRE_CONTEXT(t);
const char *key, *value;
time_t new_stamp;
bool update_dict;
int ret;
t_push();
key = t_strconcat(DICT_PATH_SHARED, expire.username, "/",
t->box->name, NULL);
if (xt->first_expunged) {
/* first mail expunged. dict needs updating. */
update_dict = first_nonexpunged_timestamp(t, &new_stamp) == 0;
} else {
/* saved new mails. dict needs to be updated only if this is
the first mail in the database */
ret = dict_lookup(expire.db, pool_datastack_create(),
key, &value);
update_dict = ret == 0 || strtoul(value, NULL, 10) == 0;
new_stamp = xt->first_save_time;
}
mail_free(&xt->mail);
i_free(xt);
if (xpr_box->super.transaction_commit(t, flags) < 0) {
t_pop();
return -1;
}
if (update_dict) {
struct dict_transaction_context *dctx;
new_stamp += xpr_box->expire_secs;
dctx = dict_transaction_begin(expire.db);
dict_set(dctx, key, dec2str(new_stamp));
dict_transaction_commit(dctx);
}
t_pop();
return 0;
}
static void
expire_mailbox_transaction_rollback(struct mailbox_transaction_context *t)
{
struct expire_mailbox *xpr_box = EXPIRE_CONTEXT(t->box);
struct expire_transaction_context *xt = EXPIRE_CONTEXT(t);
mail_free(&xt->mail);
xpr_box->super.transaction_rollback(t);
i_free(xt);
}
static int expire_mail_expunge(struct mail *_mail)
{
struct mail_private *mail = (struct mail_private *)_mail;
struct expire_mail *xpr_mail = EXPIRE_CONTEXT(mail);
struct expire_transaction_context *xt =
EXPIRE_CONTEXT(_mail->transaction);
if (xpr_mail->super.expunge(_mail) < 0)
return -1;
if (_mail->seq == 1) {
/* first mail expunged, database needs to be updated */
xt->first_expunged = TRUE;
}
return 0;
}
static struct mail *
expire_mail_alloc(struct mailbox_transaction_context *t,
enum mail_fetch_field wanted_fields,
struct mailbox_header_lookup_ctx *wanted_headers)
{
struct expire_mailbox *xpr_box = EXPIRE_CONTEXT(t->box);
struct expire_mail *xpr_mail;
struct mail *_mail;
struct mail_private *mail;
_mail = xpr_box->super.mail_alloc(t, wanted_fields, wanted_headers);
mail = (struct mail_private *)_mail;
xpr_mail = p_new(mail->pool, struct expire_mail, 1);
xpr_mail->super = mail->v;
mail->v.expunge = expire_mail_expunge;
array_idx_set(&mail->module_contexts, expire.storage_module_id,
&xpr_mail);
return _mail;
}
static void
mail_set_save_time(struct mailbox_transaction_context *t, uint32_t seq)
{
struct expire_transaction_context *xt = EXPIRE_CONTEXT(t);
struct index_transaction_context *it =
(struct index_transaction_context *)t;
if (xt->first_save_time == 0)
xt->first_save_time = ioloop_time;
mail_cache_add(it->cache_trans, seq, MAIL_CACHE_SAVE_DATE,
&ioloop_time, sizeof(ioloop_time));
}
static int
expire_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,
struct mail *dest_mail, struct mail_save_context **ctx_r)
{
struct expire_transaction_context *xt = EXPIRE_CONTEXT(t);
struct expire_mailbox *xpr_box = EXPIRE_CONTEXT(t->box);
int ret;
if (dest_mail == NULL)
dest_mail = xt->mail;
ret = xpr_box->super.save_init(t, flags, keywords, received_date,
timezone_offset, from_envelope, input,
dest_mail, ctx_r);
if (ret >= 0)
mail_set_save_time(t, dest_mail->seq);
return ret;
}
static int
expire_copy(struct mailbox_transaction_context *t, struct mail *mail,
enum mail_flags flags, struct mail_keywords *keywords,
struct mail *dest_mail)
{
struct expire_transaction_context *xt = EXPIRE_CONTEXT(t);
struct expire_mailbox *xpr_box = EXPIRE_CONTEXT(t->box);
int ret;
if (dest_mail == NULL)
dest_mail = xt->mail;
ret = xpr_box->super.copy(t, mail, flags, keywords, dest_mail);
if (ret >= 0)
mail_set_save_time(t, dest_mail->seq);
return ret;
}
static void mailbox_expire_hook(struct mailbox *box, time_t expire_secs)
{
struct expire_mailbox *xpr_box;
xpr_box = p_new(box->pool, struct expire_mailbox, 1);
xpr_box->super = box->v;
box->v.transaction_begin = expire_mailbox_transaction_begin;
box->v.transaction_commit = expire_mailbox_transaction_commit;
box->v.transaction_rollback = expire_mailbox_transaction_rollback;
box->v.mail_alloc = expire_mail_alloc;
box->v.save_init = expire_save_init;
box->v.copy = expire_copy;
xpr_box->expire_secs = expire_secs;
array_idx_set(&box->module_contexts,
expire.storage_module_id, &xpr_box);
}
static struct mailbox *
expire_mailbox_open(struct mail_storage *storage, const char *name,
struct istream *input, enum mailbox_open_flags flags)
{
struct expire_mail_storage *xpr_storage = EXPIRE_CONTEXT(storage);
struct mailbox *box;
const struct expire_box *expire_box;
box = xpr_storage->super.mailbox_open(storage, name, input, flags);
if (box != NULL) {
expire_box = expire_box_find(expire.env, name);
if (expire_box != NULL)
mailbox_expire_hook(box, expire_box->expire_secs);
}
return box;
}
static void expire_mail_storage_created(struct mail_storage *storage)
{
struct expire_mail_storage *xpr_storage;
if (expire.next_hook_mail_storage_created != NULL)
expire.next_hook_mail_storage_created(storage);
xpr_storage = p_new(storage->pool, struct expire_mail_storage, 1);
xpr_storage->super = storage->v;
storage->v.mailbox_open = expire_mailbox_open;
if (!expire.storage_module_id_set) {
expire.storage_module_id = mail_storage_module_id++;
expire.storage_module_id_set = TRUE;
}
array_idx_set(&storage->module_contexts,
expire.storage_module_id, &xpr_storage);
}
void expire_plugin_init(void)
{
const char *env, *dict_uri;
env = getenv("EXPIRE");
if (env != NULL) {
dict_uri = getenv("EXPIRE_DICT");
if (dict_uri == NULL)
i_fatal("expire plugin: expire_dict setting missing");
expire.env = expire_env_init(env);
expire.db = dict_init(dict_uri, DICT_DATA_TYPE_UINT32, NULL);
expire.username = getenv("USER");
expire.next_hook_mail_storage_created =
hook_mail_storage_created;
hook_mail_storage_created = expire_mail_storage_created;
}
}
void expire_plugin_deinit(void)
{
if (expire.db != NULL) {
hook_mail_storage_created =
expire.next_hook_mail_storage_created;
dict_deinit(&expire.db);
expire_env_deinit(expire.env);
}
}
--- NEW FILE: expire-plugin.h ---
#ifndef __EXPIRE_PLUGIN_H
#define __EXPIRE_PLUGIN_H
void expire_plugin_init(void);
void expire_plugin_deinit(void);
#endif
--- NEW FILE: expire-tool.c ---
/* Copyright (C) 2006 Timo Sirainen */
#include "lib.h"
#include "ioloop.h"
#include "randgen.h"
#include "lib-signals.h"
#include "dict-client.h"
#include "mail-search.h"
#include "mail-storage.h"
#include "auth-client.h"
#include "expire-env.h"
#include <stdlib.h>
/* ugly, but automake doesn't like having it built as both static and
dynamic object.. */
#include "expire-env.c"
#define DEFAULT_AUTH_SOCKET_PATH PKG_RUNDIR"/auth-master"
struct expire_context {
struct auth_connection *auth_conn;
char *user;
struct mail_storage *storage;
};
static int user_init(struct expire_context *ctx, const char *user)
{
enum mail_storage_flags flags;
enum mail_storage_lock_method lock_method;
const char *mail_env;
int ret;
if ((ret = auth_client_put_user_env(ctx->auth_conn, user)) <= 0) {
if (ret < 0)
return ret;
/* user no longer exists */
return 0;
}
mail_env = getenv("MAIL");
mail_storage_parse_env(&flags, &lock_method);
ctx->storage = mail_storage_create_with_data(mail_env, user,
flags, lock_method);
if (ctx->storage == NULL) {
i_error("Failed to create storage for '%s' with mail '%s'",
user, mail_env == NULL ? "(null)" : mail_env);
return -1;
}
return 1;
}
static void user_deinit(struct expire_context *ctx)
{
mail_storage_destroy(&ctx->storage);
i_free_and_null(ctx->user);
}
static int
mailbox_delete_old_mails(struct expire_context *ctx, const char *user,
const char *mailbox, time_t expire_secs,
time_t *oldest_r)
{
struct mailbox *box;
struct mail_search_context *search_ctx;
struct mailbox_transaction_context *t;
struct mail_search_arg search_arg;
struct mail *mail;
time_t now, save_time;
int ret = 0;
*oldest_r = 0;
if (ctx->user != NULL && strcmp(user, ctx->user) != 0)
user_deinit(ctx);
if (ctx->user == NULL) {
if ((ret = user_init(ctx, user)) <= 0)
return ret;
ctx->user = i_strdup(user);
}
memset(&search_arg, 0, sizeof(search_arg));
search_arg.type = SEARCH_ALL;
search_arg.next = NULL;
box = mailbox_open(ctx->storage, mailbox, NULL, 0);
t = mailbox_transaction_begin(box, 0);
search_ctx = mailbox_search_init(t, NULL, &search_arg, NULL);
mail = mail_alloc(t, 0, NULL);
now = time(NULL);
while (mailbox_search_next(search_ctx, mail) > 0) {
save_time = mail_get_save_date(mail);
if (save_time == (time_t)-1) {
/* maybe just got expunged. anyway try again later. */
ret = -1;
break;
}
if (save_time + expire_secs <= now) {
if (mail_expunge(mail) < 0) {
ret = -1;
break;
}
} else {
/* first non-expunged one. */
*oldest_r = save_time;
break;
}
}
mail_free(&mail);
if (mailbox_search_deinit(&search_ctx) < 0)
ret = -1;
if (mailbox_transaction_commit(&t, MAILBOX_SYNC_FLAG_FULL_READ |
MAILBOX_SYNC_FLAG_FULL_WRITE) < 0)
ret = -1;
mailbox_close(&box);
return ret < 0 ? -1 : 0;
}
static void expire_run(void)
{
struct expire_context ctx;
struct dict *dict = NULL;
struct dict_transaction_context *trans;
struct dict_iterate_context *iter;
struct expire_env *env;
const struct expire_box *expire_box;
time_t oldest;
const char *auth_socket, *p, *key, *value;
const char *username, *mailbox;
dict_driver_register(&dict_driver_client);
mail_storage_init();
mail_storage_register_all();
if (getenv("EXPIRE") == NULL)
i_fatal("expire setting not set");
if (getenv("EXPIRE_DICT") == NULL)
i_fatal("expire_dict setting not set");
auth_socket = getenv("AUTH_SOCKET_PATH");
if (auth_socket == NULL)
auth_socket = DEFAULT_AUTH_SOCKET_PATH;
memset(&ctx, 0, sizeof(ctx));
ctx.auth_conn = auth_connection_init(auth_socket);
env = expire_env_init(getenv("EXPIRE"));
dict = dict_init(getenv("EXPIRE_DICT"), DICT_DATA_TYPE_UINT32, "");
trans = dict_transaction_begin(dict);
iter = dict_iterate_init(dict, DICT_PATH_SHARED,
DICT_ITERATE_FLAG_SORT_BY_VALUE);
/* We'll get the oldest values (timestamps) first */
while (dict_iterate(iter, &key, &value) > 0) {
/* key = DICT_PATH_SHARED<user>/<mailbox> */
username = key + strlen(DICT_PATH_SHARED);
p = strchr(username, '/');
if (p == NULL) {
i_error("Expire dictionary contains invalid key: %s",
key);
continue;
}
t_push();
username = t_strdup_until(username, p);
mailbox = p + 1;
expire_box = expire_box_find(env, mailbox);
if (expire_box == NULL) {
/* we're no longer expunging old messages from here */
dict_unset(trans, key);
} else if (now < strtoul(value, NULL, 10)) {
/* this and the rest of the timestamps are in future,
so stop processing */
t_pop();
break;
} else {
if (mailbox_delete_old_mails(&ctx, username, mailbox,
expire_box->expire_secs,
&oldest) == 0) {
/* successful update */
if (oldest == 0) {
/* no more messages or we're no longer
expunging messages from here */
dict_unset(trans, key);
} else {
const char *new_value;
oldest += expire_box->expire_secs;
new_value = dec2str(oldest);
if (strcmp(value, new_value) != 0)
dict_set(trans, key, new_value);
}
}
}
t_pop();
}
dict_iterate_deinit(iter);
dict_transaction_commit(trans);
dict_deinit(&dict);
if (ctx.user != NULL)
user_deinit(&ctx);
auth_connection_deinit(ctx.auth_conn);
mail_storage_deinit();
dict_driver_unregister(&dict_driver_client);
}
int main(void)
{
struct ioloop *ioloop;
lib_init();
lib_signals_init();
random_init();
ioloop = io_loop_create(system_pool);
expire_run();
io_loop_destroy(&ioloop);
lib_signals_deinit();
lib_deinit();
return 0;
}
More information about the dovecot-cvs
mailing list