trash-plugin.c revision bd8f849ab303e0da30a0383107a1b8078aece4f0
5aefb6555731130ca4fd295960123d71f2d21fe8rie/* Copyright (C) 2005 Timo Sirainen */
5aefb6555731130ca4fd295960123d71f2d21fe8rie
5aefb6555731130ca4fd295960123d71f2d21fe8rie#include "lib.h"
5aefb6555731130ca4fd295960123d71f2d21fe8rie#include "array.h"
5aefb6555731130ca4fd295960123d71f2d21fe8rie#include "istream.h"
5aefb6555731130ca4fd295960123d71f2d21fe8rie#include "home-expand.h"
5aefb6555731130ca4fd295960123d71f2d21fe8rie#include "mail-search.h"
5aefb6555731130ca4fd295960123d71f2d21fe8rie#include "quota-private.h"
5aefb6555731130ca4fd295960123d71f2d21fe8rie#include "quota-plugin.h"
5aefb6555731130ca4fd295960123d71f2d21fe8rie#include "trash-plugin.h"
5aefb6555731130ca4fd295960123d71f2d21fe8rie
5aefb6555731130ca4fd295960123d71f2d21fe8rie#include <stdlib.h>
5aefb6555731130ca4fd295960123d71f2d21fe8rie#include <unistd.h>
5aefb6555731130ca4fd295960123d71f2d21fe8rie#include <fcntl.h>
5aefb6555731130ca4fd295960123d71f2d21fe8rie
5aefb6555731130ca4fd295960123d71f2d21fe8rie#define MAX_RETRY_COUNT 3
5aefb6555731130ca4fd295960123d71f2d21fe8rie
5aefb6555731130ca4fd295960123d71f2d21fe8riestruct trash_mailbox {
5aefb6555731130ca4fd295960123d71f2d21fe8rie const char *name;
5aefb6555731130ca4fd295960123d71f2d21fe8rie int priority; /* lower number = higher priority */
5aefb6555731130ca4fd295960123d71f2d21fe8rie
5aefb6555731130ca4fd295960123d71f2d21fe8rie struct mail_storage *storage;
7e16fca05dfbcfd32c2ebc9e4d1abdac1cd8657cAli Bahrami
5aefb6555731130ca4fd295960123d71f2d21fe8rie /* temporarily set while cleaning: */
5aefb6555731130ca4fd295960123d71f2d21fe8rie struct mailbox *box;
5aefb6555731130ca4fd295960123d71f2d21fe8rie struct mailbox_transaction_context *trans;
5aefb6555731130ca4fd295960123d71f2d21fe8rie struct mail_search_arg search_arg;
5aefb6555731130ca4fd295960123d71f2d21fe8rie struct mail_search_context *search_ctx;
5aefb6555731130ca4fd295960123d71f2d21fe8rie struct mail *mail;
ba2be53024c0b999e74ba9adcd7d80fec5df8c57ab
5aefb6555731130ca4fd295960123d71f2d21fe8rie unsigned int mail_set:1;
5aefb6555731130ca4fd295960123d71f2d21fe8rie};
5aefb6555731130ca4fd295960123d71f2d21fe8rie
5aefb6555731130ca4fd295960123d71f2d21fe8riestatic int (*trash_next_quota_test_alloc)(struct quota_transaction_context *,
5aefb6555731130ca4fd295960123d71f2d21fe8rie uoff_t, bool *);
5aefb6555731130ca4fd295960123d71f2d21fe8rie
5aefb6555731130ca4fd295960123d71f2d21fe8riestatic pool_t config_pool;
5aefb6555731130ca4fd295960123d71f2d21fe8rie/* trash_boxes ordered by priority, highest first */
5aefb6555731130ca4fd295960123d71f2d21fe8riestatic ARRAY_DEFINE(trash_boxes, struct trash_mailbox);
981a172d53aeb81520fcfecf6ab2615277c7bd18ab
981a172d53aeb81520fcfecf6ab2615277c7bd18abstatic int sync_mailbox(struct mailbox *box)
981a172d53aeb81520fcfecf6ab2615277c7bd18ab{
981a172d53aeb81520fcfecf6ab2615277c7bd18ab struct mailbox_sync_context *ctx;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab struct mailbox_sync_rec sync_rec;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab struct mailbox_status status;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab
981a172d53aeb81520fcfecf6ab2615277c7bd18ab ctx = mailbox_sync_init(box, MAILBOX_SYNC_FLAG_FULL_READ);
981a172d53aeb81520fcfecf6ab2615277c7bd18ab while (mailbox_sync_next(ctx, &sync_rec) > 0)
981a172d53aeb81520fcfecf6ab2615277c7bd18ab ;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab return mailbox_sync_deinit(&ctx, &status);
981a172d53aeb81520fcfecf6ab2615277c7bd18ab}
981a172d53aeb81520fcfecf6ab2615277c7bd18ab
4f680cc668fa6cf678c531083400ade9a9c7934cAli Bahramistatic int trash_clean_mailbox_open(struct trash_mailbox *trash)
4f680cc668fa6cf678c531083400ade9a9c7934cAli Bahrami{
981a172d53aeb81520fcfecf6ab2615277c7bd18ab trash->box = mailbox_open(trash->storage, trash->name, NULL,
981a172d53aeb81520fcfecf6ab2615277c7bd18ab MAILBOX_OPEN_KEEP_RECENT);
981a172d53aeb81520fcfecf6ab2615277c7bd18ab if (sync_mailbox(trash->box) < 0)
981a172d53aeb81520fcfecf6ab2615277c7bd18ab return -1;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab
981a172d53aeb81520fcfecf6ab2615277c7bd18ab trash->trans = mailbox_transaction_begin(trash->box, 0);
981a172d53aeb81520fcfecf6ab2615277c7bd18ab
981a172d53aeb81520fcfecf6ab2615277c7bd18ab trash->search_ctx = mailbox_search_init(trash->trans, NULL,
981a172d53aeb81520fcfecf6ab2615277c7bd18ab &trash->search_arg, NULL);
981a172d53aeb81520fcfecf6ab2615277c7bd18ab trash->mail = mail_alloc(trash->trans, MAIL_FETCH_PHYSICAL_SIZE |
981a172d53aeb81520fcfecf6ab2615277c7bd18ab MAIL_FETCH_RECEIVED_DATE, NULL);
981a172d53aeb81520fcfecf6ab2615277c7bd18ab
981a172d53aeb81520fcfecf6ab2615277c7bd18ab return mailbox_search_next(trash->search_ctx, trash->mail);
981a172d53aeb81520fcfecf6ab2615277c7bd18ab}
981a172d53aeb81520fcfecf6ab2615277c7bd18ab
981a172d53aeb81520fcfecf6ab2615277c7bd18abstatic int trash_clean_mailbox_get_next(struct trash_mailbox *trash,
981a172d53aeb81520fcfecf6ab2615277c7bd18ab time_t *received_time_r)
981a172d53aeb81520fcfecf6ab2615277c7bd18ab{
981a172d53aeb81520fcfecf6ab2615277c7bd18ab int ret;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab
981a172d53aeb81520fcfecf6ab2615277c7bd18ab if (!trash->mail_set) {
981a172d53aeb81520fcfecf6ab2615277c7bd18ab if (trash->box == NULL)
981a172d53aeb81520fcfecf6ab2615277c7bd18ab ret = trash_clean_mailbox_open(trash);
4f680cc668fa6cf678c531083400ade9a9c7934cAli Bahrami else
981a172d53aeb81520fcfecf6ab2615277c7bd18ab ret = mailbox_search_next(trash->search_ctx,
4f680cc668fa6cf678c531083400ade9a9c7934cAli Bahrami trash->mail);
981a172d53aeb81520fcfecf6ab2615277c7bd18ab if (ret <= 0)
981a172d53aeb81520fcfecf6ab2615277c7bd18ab return ret;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab
4f680cc668fa6cf678c531083400ade9a9c7934cAli Bahrami trash->mail_set = TRUE;
4f680cc668fa6cf678c531083400ade9a9c7934cAli Bahrami }
981a172d53aeb81520fcfecf6ab2615277c7bd18ab
981a172d53aeb81520fcfecf6ab2615277c7bd18ab *received_time_r = mail_get_received_date(trash->mail);
981a172d53aeb81520fcfecf6ab2615277c7bd18ab return 1;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab}
981a172d53aeb81520fcfecf6ab2615277c7bd18ab
981a172d53aeb81520fcfecf6ab2615277c7bd18abstatic int trash_try_clean_mails(uint64_t size_needed)
981a172d53aeb81520fcfecf6ab2615277c7bd18ab{
981a172d53aeb81520fcfecf6ab2615277c7bd18ab struct trash_mailbox *trashes;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab unsigned int i, j, count, oldest_idx;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab time_t oldest, received;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab uint64_t size;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab int ret = 0;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab
981a172d53aeb81520fcfecf6ab2615277c7bd18ab trashes = array_get_modifiable(&trash_boxes, &count);
5aefb6555731130ca4fd295960123d71f2d21fe8rie for (i = 0; i < count; ) {
981a172d53aeb81520fcfecf6ab2615277c7bd18ab if (trashes[i].storage == NULL) {
5aefb6555731130ca4fd295960123d71f2d21fe8rie /* FIXME: this is really ugly. it'll do however until
5aefb6555731130ca4fd295960123d71f2d21fe8rie we get proper namespace support for lib-storage. */
5aefb6555731130ca4fd295960123d71f2d21fe8rie struct mail_storage *const *storage;
f5a18a30a06b60eec275589214da939abbaa99d9rie
5aefb6555731130ca4fd295960123d71f2d21fe8rie storage = array_idx(&quota->storages, 0);
5aefb6555731130ca4fd295960123d71f2d21fe8rie trashes[i].storage = *storage;
5aefb6555731130ca4fd295960123d71f2d21fe8rie }
31fdd7ca2d295948f9f1bcc2a1178c66467bca63ab /* expunge oldest mails first in all trash boxes with
5aefb6555731130ca4fd295960123d71f2d21fe8rie same priority */
5aefb6555731130ca4fd295960123d71f2d21fe8rie oldest_idx = count;
5aefb6555731130ca4fd295960123d71f2d21fe8rie oldest = (time_t)-1;
5aefb6555731130ca4fd295960123d71f2d21fe8rie for (j = i; j < count; j++) {
5aefb6555731130ca4fd295960123d71f2d21fe8rie if (trashes[j].priority != trashes[j].priority)
5aefb6555731130ca4fd295960123d71f2d21fe8rie break;
5aefb6555731130ca4fd295960123d71f2d21fe8rie
5aefb6555731130ca4fd295960123d71f2d21fe8rie ret = trash_clean_mailbox_get_next(&trashes[i],
5aefb6555731130ca4fd295960123d71f2d21fe8rie &received);
5aefb6555731130ca4fd295960123d71f2d21fe8rie if (ret < 0)
5aefb6555731130ca4fd295960123d71f2d21fe8rie goto __err;
5aefb6555731130ca4fd295960123d71f2d21fe8rie if (ret > 0) {
5aefb6555731130ca4fd295960123d71f2d21fe8rie if (oldest == (time_t)-1 ||
981a172d53aeb81520fcfecf6ab2615277c7bd18ab received < oldest) {
981a172d53aeb81520fcfecf6ab2615277c7bd18ab oldest = received;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab oldest_idx = j;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab }
981a172d53aeb81520fcfecf6ab2615277c7bd18ab }
981a172d53aeb81520fcfecf6ab2615277c7bd18ab }
981a172d53aeb81520fcfecf6ab2615277c7bd18ab
981a172d53aeb81520fcfecf6ab2615277c7bd18ab if (oldest_idx < count) {
981a172d53aeb81520fcfecf6ab2615277c7bd18ab if (mail_expunge(trashes[oldest_idx].mail) < 0)
981a172d53aeb81520fcfecf6ab2615277c7bd18ab break;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab
981a172d53aeb81520fcfecf6ab2615277c7bd18ab size = mail_get_physical_size(trashes[oldest_idx].mail);
981a172d53aeb81520fcfecf6ab2615277c7bd18ab if (size >= size_needed) {
981a172d53aeb81520fcfecf6ab2615277c7bd18ab size_needed = 0;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab break;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab }
981a172d53aeb81520fcfecf6ab2615277c7bd18ab trashes[oldest_idx].mail_set = FALSE;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab
981a172d53aeb81520fcfecf6ab2615277c7bd18ab size_needed -= size;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab } else {
981a172d53aeb81520fcfecf6ab2615277c7bd18ab /* find more mails from next priority's mailbox */
981a172d53aeb81520fcfecf6ab2615277c7bd18ab i = j;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab }
981a172d53aeb81520fcfecf6ab2615277c7bd18ab }
981a172d53aeb81520fcfecf6ab2615277c7bd18ab
981a172d53aeb81520fcfecf6ab2615277c7bd18ab__err:
981a172d53aeb81520fcfecf6ab2615277c7bd18ab for (i = 0; i < count; i++) {
981a172d53aeb81520fcfecf6ab2615277c7bd18ab struct trash_mailbox *trash = &trashes[i];
981a172d53aeb81520fcfecf6ab2615277c7bd18ab
981a172d53aeb81520fcfecf6ab2615277c7bd18ab trash->mail_set = FALSE;
981a172d53aeb81520fcfecf6ab2615277c7bd18ab mail_free(&trash->mail);
981a172d53aeb81520fcfecf6ab2615277c7bd18ab (void)mailbox_search_deinit(&trash->search_ctx);
981a172d53aeb81520fcfecf6ab2615277c7bd18ab
981a172d53aeb81520fcfecf6ab2615277c7bd18ab if (size_needed == 0) {
981a172d53aeb81520fcfecf6ab2615277c7bd18ab (void)mailbox_transaction_commit(&trash->trans,
981a172d53aeb81520fcfecf6ab2615277c7bd18ab MAILBOX_SYNC_FLAG_FULL_WRITE);
5aefb6555731130ca4fd295960123d71f2d21fe8rie } else {
c6c9aed4d309e3d11be652b85e3bf8bb72f20c87ab /* couldn't get enough space, don't expunge anything */
c6c9aed4d309e3d11be652b85e3bf8bb72f20c87ab mailbox_transaction_rollback(&trash->trans);
c6c9aed4d309e3d11be652b85e3bf8bb72f20c87ab }
c6c9aed4d309e3d11be652b85e3bf8bb72f20c87ab
c6c9aed4d309e3d11be652b85e3bf8bb72f20c87ab mailbox_close(&trash->box);
c6c9aed4d309e3d11be652b85e3bf8bb72f20c87ab }
c6c9aed4d309e3d11be652b85e3bf8bb72f20c87ab return size_needed == 0;
c6c9aed4d309e3d11be652b85e3bf8bb72f20c87ab}
c6c9aed4d309e3d11be652b85e3bf8bb72f20c87ab
c6c9aed4d309e3d11be652b85e3bf8bb72f20c87abstatic int
c6c9aed4d309e3d11be652b85e3bf8bb72f20c87abtrash_quota_test_alloc(struct quota_transaction_context *ctx,
c6c9aed4d309e3d11be652b85e3bf8bb72f20c87ab uoff_t size, bool *too_large_r)
5aefb6555731130ca4fd295960123d71f2d21fe8rie{
5aefb6555731130ca4fd295960123d71f2d21fe8rie int ret, i;
5aefb6555731130ca4fd295960123d71f2d21fe8rie
5aefb6555731130ca4fd295960123d71f2d21fe8rie for (i = 0; ; i++) {
5aefb6555731130ca4fd295960123d71f2d21fe8rie ret = trash_next_quota_test_alloc(ctx, size, too_large_r);
5aefb6555731130ca4fd295960123d71f2d21fe8rie if (ret != 0 || *too_large_r)
5aefb6555731130ca4fd295960123d71f2d21fe8rie return ret;
39773e466ff90ce703d7f52f3267d7e96c09c6f5ab
39773e466ff90ce703d7f52f3267d7e96c09c6f5ab if (i == MAX_RETRY_COUNT) {
5aefb6555731130ca4fd295960123d71f2d21fe8rie /* trash_try_clean_mails() should have returned 0 if
5aefb6555731130ca4fd295960123d71f2d21fe8rie it couldn't get enough space, but allow retrying
5aefb6555731130ca4fd295960123d71f2d21fe8rie it a couple of times if there was some extra space
5aefb6555731130ca4fd295960123d71f2d21fe8rie that was needed.. */
5aefb6555731130ca4fd295960123d71f2d21fe8rie break;
5aefb6555731130ca4fd295960123d71f2d21fe8rie }
5aefb6555731130ca4fd295960123d71f2d21fe8rie
5aefb6555731130ca4fd295960123d71f2d21fe8rie /* not enough space. try deleting some from mailbox. */
5aefb6555731130ca4fd295960123d71f2d21fe8rie ret = trash_try_clean_mails(size);
5aefb6555731130ca4fd295960123d71f2d21fe8rie if (ret <= 0)
5aefb6555731130ca4fd295960123d71f2d21fe8rie return 0;
5aefb6555731130ca4fd295960123d71f2d21fe8rie }
5aefb6555731130ca4fd295960123d71f2d21fe8rie
5aefb6555731130ca4fd295960123d71f2d21fe8rie return 0;
5aefb6555731130ca4fd295960123d71f2d21fe8rie}
5aefb6555731130ca4fd295960123d71f2d21fe8rie
5aefb6555731130ca4fd295960123d71f2d21fe8riestatic int trash_mailbox_priority_cmp(const void *p1, const void *p2)
5aefb6555731130ca4fd295960123d71f2d21fe8rie{
5aefb6555731130ca4fd295960123d71f2d21fe8rie const struct trash_mailbox *t1 = p1, *t2 = p2;
5aefb6555731130ca4fd295960123d71f2d21fe8rie
5aefb6555731130ca4fd295960123d71f2d21fe8rie return t1->priority - t2->priority;
5aefb6555731130ca4fd295960123d71f2d21fe8rie}
39773e466ff90ce703d7f52f3267d7e96c09c6f5ab
39773e466ff90ce703d7f52f3267d7e96c09c6f5abstatic int read_configuration(const char *path)
5aefb6555731130ca4fd295960123d71f2d21fe8rie{
5aefb6555731130ca4fd295960123d71f2d21fe8rie struct istream *input;
5aefb6555731130ca4fd295960123d71f2d21fe8rie const char *line, *name;
5aefb6555731130ca4fd295960123d71f2d21fe8rie struct trash_mailbox *trash;
5aefb6555731130ca4fd295960123d71f2d21fe8rie unsigned int count;
5aefb6555731130ca4fd295960123d71f2d21fe8rie int fd;
5aefb6555731130ca4fd295960123d71f2d21fe8rie
5aefb6555731130ca4fd295960123d71f2d21fe8rie fd = open(path, O_RDONLY);
5aefb6555731130ca4fd295960123d71f2d21fe8rie if (fd == -1) {
5aefb6555731130ca4fd295960123d71f2d21fe8rie i_error("open(%s) failed: %m", path);
5aefb6555731130ca4fd295960123d71f2d21fe8rie return -1;
5aefb6555731130ca4fd295960123d71f2d21fe8rie }
5aefb6555731130ca4fd295960123d71f2d21fe8rie
5aefb6555731130ca4fd295960123d71f2d21fe8rie p_clear(config_pool);
5aefb6555731130ca4fd295960123d71f2d21fe8rie p_array_init(&trash_boxes, config_pool, 8);
5aefb6555731130ca4fd295960123d71f2d21fe8rie
5aefb6555731130ca4fd295960123d71f2d21fe8rie input = i_stream_create_file(fd, default_pool, (size_t)-1, FALSE);
5aefb6555731130ca4fd295960123d71f2d21fe8rie while ((line = i_stream_read_next_line(input)) != NULL) {
5aefb6555731130ca4fd295960123d71f2d21fe8rie /* <priority> <mailbox name> */
5aefb6555731130ca4fd295960123d71f2d21fe8rie name = strchr(line, ' ');
c6c9aed4d309e3d11be652b85e3bf8bb72f20c87ab if (name == NULL || name[1] == '\0')
7e16fca05dfbcfd32c2ebc9e4d1abdac1cd8657cAli Bahrami continue;
7e16fca05dfbcfd32c2ebc9e4d1abdac1cd8657cAli Bahrami
7e16fca05dfbcfd32c2ebc9e4d1abdac1cd8657cAli Bahrami trash = array_append_space(&trash_boxes);
c6c9aed4d309e3d11be652b85e3bf8bb72f20c87ab trash->name = p_strdup(config_pool, name+1);
39773e466ff90ce703d7f52f3267d7e96c09c6f5ab trash->priority = atoi(t_strdup_until(line, name));
c6c9aed4d309e3d11be652b85e3bf8bb72f20c87ab trash->search_arg.type = SEARCH_ALL;
39773e466ff90ce703d7f52f3267d7e96c09c6f5ab }
c6c9aed4d309e3d11be652b85e3bf8bb72f20c87ab i_stream_destroy(&input);
39773e466ff90ce703d7f52f3267d7e96c09c6f5ab (void)close(fd);
39773e466ff90ce703d7f52f3267d7e96c09c6f5ab
39773e466ff90ce703d7f52f3267d7e96c09c6f5ab trash = array_get_modifiable(&trash_boxes, &count);
39773e466ff90ce703d7f52f3267d7e96c09c6f5ab qsort(trash, count, sizeof(*trash), trash_mailbox_priority_cmp);
4f680cc668fa6cf678c531083400ade9a9c7934cAli Bahrami return 0;
4f680cc668fa6cf678c531083400ade9a9c7934cAli Bahrami}
4f680cc668fa6cf678c531083400ade9a9c7934cAli Bahrami
4f680cc668fa6cf678c531083400ade9a9c7934cAli Bahramivoid trash_plugin_init(void)
5aefb6555731130ca4fd295960123d71f2d21fe8rie{
5aefb6555731130ca4fd295960123d71f2d21fe8rie const char *env;
5aefb6555731130ca4fd295960123d71f2d21fe8rie
5aefb6555731130ca4fd295960123d71f2d21fe8rie env = getenv("TRASH");
5aefb6555731130ca4fd295960123d71f2d21fe8rie if (env == NULL)
5aefb6555731130ca4fd295960123d71f2d21fe8rie return;
if (quota == NULL) {
i_error("trash plugin: quota plugin not initialized");
return;
}
config_pool = pool_alloconly_create("trash config", 1024);
if (read_configuration(env) < 0)
return;
trash_next_quota_test_alloc = quota->test_alloc;
quota->test_alloc = trash_quota_test_alloc;
}
void trash_plugin_deinit(void)
{
quota->test_alloc = trash_next_quota_test_alloc;
if (config_pool != NULL)
pool_unref(config_pool);
}