#include <config.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <syslog.h>
#include <errno.h>
#include <signal.h>
#include "annotate.h"
#include "cyrusdb.h"
#include "duplicate.h"
#include "exitcodes.h"
#include "global.h"
#include "hash.h"
#include "libcyr_cfg.h"
#include "mboxlist.h"
#include "util.h"
#include "xmalloc.h"
#include "xstrlcpy.h"
#include "xstrlcat.h"
const int config_need_data = 0;
void usage(void)
{
fprintf(stderr,
"cyr_expire [-C <altconfig>] -E <days> [-X <expunge-days>] [-v]\n");
exit(-1);
}
struct expire_rock {
struct hash_table *table;
enum enum_value expunge_mode;
time_t expire_mark;
time_t expunge_mark;
unsigned long mailboxes;
unsigned long messages;
unsigned long deleted;
int verbose;
};
static int expire_cb(struct mailbox *mailbox __attribute__((unused)),
void *rock, char *index, int expunge_flags)
{
struct expire_rock *erock = (struct expire_rock *) rock;
bit32 tstamp;
erock->messages++;
if (expunge_flags & EXPUNGE_CLEANUP) {
tstamp = ntohl(*((bit32 *)(index+OFFSET_LAST_UPDATED)));
if (tstamp < erock->expunge_mark) {
erock->deleted++;
return 1;
}
} else {
tstamp = ntohl(*((bit32 *)(index+OFFSET_SENTDATE)));
if (tstamp < erock->expire_mark) {
erock->deleted++;
return 1;
}
}
return 0;
}
int expire(char *name, int matchlen, int maycreate __attribute__((unused)),
void *rock)
{
struct expire_rock *erock = (struct expire_rock *) rock;
char buf[MAX_MAILBOX_NAME+1] = "", *p;
struct annotation_data attrib;
int r, domainlen = 0;
int mbtype;
char *path, *mpath;
r = mboxlist_detail(name, &mbtype, &path, &mpath, NULL, NULL, NULL);
if (r) {
if (erock->verbose) {
printf("error looking up %s: %s\n", name, error_message(r));
}
return 1;
}
if (mbtype & MBTYPE_REMOTE) return 0;
if (config_virtdomains && (p = strchr(name, '!')))
domainlen = p - name + 1;
strncpy(buf, name, matchlen);
buf[matchlen] = '\0';
while (1) {
r = annotatemore_lookup(buf, "/vendor/cmu/cyrus-imapd/expire", "",
&attrib);
if (r ||
attrib.value ||
!buf[0] ||
!strcmp(buf+domainlen, "user")) {
break;
}
p = strrchr(buf, '.');
if (p && (p - buf > domainlen))
*p = '\0';
else if (!buf[domainlen])
buf[0] = '\0';
else
buf[domainlen] = '\0';
}
if (!r && (attrib.value ||
erock->expunge_mode != IMAP_ENUM_EXPUNGE_MODE_IMMEDIATE)) {
struct mailbox mailbox;
int doclose = 0;
int expunge_flags = 0;
if (!attrib.value &&
erock->expunge_mode != IMAP_ENUM_EXPUNGE_MODE_IMMEDIATE) {
char fnamebuf[MAX_MAILBOX_PATH+1];
struct stat sbuf;
if (mpath &&
(config_metapartition_files &
IMAP_ENUM_METAPARTITION_FILES_EXPUNGE)) {
strlcpy(fnamebuf, mpath, sizeof(fnamebuf));
} else {
strlcpy(fnamebuf, path, sizeof(fnamebuf));
}
strlcat(fnamebuf, FNAME_EXPUNGE_INDEX, sizeof(fnamebuf));
if (stat(fnamebuf, &sbuf)) return 0;
expunge_flags |= EXPUNGE_CLEANUP;
}
r = mailbox_open_header(name, 0, &mailbox);
if (!r && mailbox.header_fd != -1) {
doclose = 1;
(void) mailbox_lock_header(&mailbox);
mailbox.header_lock_count = 1;
}
if (!r) r = mailbox_open_index(&mailbox);
if (!r) {
(void) mailbox_lock_index(&mailbox);
mailbox.index_lock_count = 1;
}
if (r) {
syslog(LOG_WARNING, "unable to open/lock mailbox %s", name);
if (doclose) mailbox_close(&mailbox);
return 0;
}
erock->mailboxes++;
erock->expire_mark = 0;
if (attrib.value) {
unsigned long expire_days = strtoul(attrib.value, NULL, 10);
time_t *expire_mark = (time_t *) xmalloc(sizeof(time_t));
*expire_mark = expire_days ?
time(0) - (expire_days * 60 * 60 * 24) : 0 ;
hash_insert(name, (void *) expire_mark, erock->table);
if (erock->verbose) {
fprintf(stderr,
"expiring messages in %s older than %ld days\n",
name, expire_days);
}
erock->expire_mark = *expire_mark;
expunge_flags |= EXPUNGE_FORCE;
}
r = mailbox_expunge(&mailbox, expire_cb, erock, expunge_flags);
mailbox_close(&mailbox);
}
if (r) {
syslog(LOG_WARNING, "failure expiring %s: %s", name, error_message(r));
}
return 0;
}
int main(int argc, char *argv[])
{
extern char *optarg;
int opt, r = 0, expire_days = 0, expunge_days = 0;
char *alt_config = NULL;
char buf[100];
struct hash_table expire_table;
struct expire_rock erock;
if (geteuid() == 0) fatal("must run as the Cyrus user", EC_USAGE);
memset(&erock, 0, sizeof(erock));
while ((opt = getopt(argc, argv, "C:E:X:v")) != EOF) {
switch (opt) {
case 'C':
alt_config = optarg;
break;
case 'E':
if (expire_days) usage();
expire_days = atoi(optarg);
break;
case 'X':
if (expunge_days) usage();
expunge_days = atoi(optarg);
break;
case 'v':
erock.verbose++;
break;
default:
usage();
break;
}
}
if (!expire_days) usage();
cyrus_init(alt_config, "cyr_expire", 0);
annotatemore_init(0, NULL, NULL);
annotatemore_open(NULL);
mboxlist_init(0);
mboxlist_open(NULL);
quotadb_init(0);
quotadb_open(NULL);
if (duplicate_init(NULL, 0) != 0) {
fprintf(stderr,
"cyr_expire: unable to init duplicate delivery database\n");
exit(1);
}
construct_hash_table(&expire_table, 10000, 1);
erock.table = &expire_table;
erock.expunge_mode = config_getenum(IMAPOPT_EXPUNGE_MODE);
erock.expunge_mark = time(0) - (expunge_days * 60 * 60 * 24);
if (erock.verbose &&
erock.expunge_mode != IMAP_ENUM_EXPUNGE_MODE_IMMEDIATE) {
fprintf(stderr,
"expunging deleted messages in mailboxes older than %d days\n",
expunge_days);
}
strlcpy(buf, "*", sizeof(buf));
mboxlist_findall(NULL, buf, 1, 0, 0, &expire, &erock);
syslog(LOG_NOTICE, "expunged %lu out of %lu messages from %lu mailboxes",
erock.deleted, erock.messages, erock.mailboxes);
if (erock.verbose) {
fprintf(stderr, "\nexpunged %lu out of %lu messages from %lu mailboxes\n",
erock.deleted, erock.messages, erock.mailboxes);
}
r = duplicate_prune(expire_days, &expire_table);
free_hash_table(&expire_table, free);
quotadb_close();
quotadb_done();
mboxlist_close();
mboxlist_done();
annotatemore_close();
annotatemore_done();
duplicate_done();
cyrus_done();
exit(r);
}