On Tue, 24 Jul 2007 09:25:28 +0100 (BST), "David Carter" <[EMAIL PROTECTED]>
said:
> On Mon, 23 Jul 2007, Bron Gondwana wrote:
> Afraid that virtual domains are something that I tend to forgot about, as
> we don't use them.
Yeah, and they're sort of scattered everywhere too!
> > Also, I prefer a different naming scheme for the deleted folders:
> >
> > [EMAIL PROTECTED]
> >
> > becomes:
> >
> > [EMAIL PROTECTED]
>
> I think that you really need some kind of timestamp so that admin users
> logged in using IMAP can order the mailboxes. It looks like this is where
> you ended up anyway. I agree that DELETED.<name>.<timestamp> is better
> than DELETED.<timestamp>.<name>.
In that case my patch is pretty close to what you'll need :) Also see the
attached with one more change, it now does sync_log_mailbox_double on the
old and new mailbox names, otherwise the DELETED mailbox never gets synced.
Bron ( still need to patch sync_client to sync expunged messages... )
--
Bron Gondwana
[EMAIL PROTECTED]
diff -ur cyrus-imapd-2.3.8.orig/imap/cyr_expire.c
cyrus-imapd-2.3.8/imap/cyr_expire.c
--- cyrus-imapd-2.3.8.orig/imap/cyr_expire.c 2007-02-05 13:41:46.000000000
-0500
+++ cyrus-imapd-2.3.8/imap/cyr_expire.c 2007-07-23 09:08:38.000000000 -0400
@@ -90,6 +90,20 @@
int verbose;
};
+struct delete_node {
+ struct delete_node *next;
+ char *name;
+};
+
+struct delete_rock {
+ char prefix[100];
+ int prefixlen;
+ time_t delete_mark;
+ struct delete_node *head;
+ struct delete_node *tail;
+ int verbose;
+};
+
/*
* mailbox_expunge() callback to expunge expired articles.
*/
@@ -266,26 +280,90 @@
return 0;
}
+int delete(char *name, int matchlen, int maycreate __attribute__((unused)),
+ void *rock)
+{
+ struct delete_rock *drock = (struct delete_rock *) rock;
+ char fnamebuf[MAX_MAILBOX_PATH+1];
+ struct stat sbuf;
+ char *p;
+ int i, r, domainlen = 0;
+ struct delete_node *node;
+ int mbtype;
+ char *path, *mpath;
+
+ if (config_virtdomains && (p = strchr(name, '!')))
+ domainlen = p - name + 1;
+
+ /* check if this is a mailbox we want to examine */
+ if (strncmp(name+domainlen, drock->prefix, drock->prefixlen))
+ return 0;
+
+ /* Skip remote mailboxes */
+ r = mboxlist_detail(name, &mbtype, &path, &mpath, NULL, NULL, NULL);
+ if (r) {
+ if (drock->verbose) {
+ printf("error looking up %s: %s\n", name, error_message(r));
+ }
+ return 1;
+ }
+ if (mbtype & MBTYPE_REMOTE) return 0;
+
+ /* check that the header is older than the number of days we care about */
+ if (mpath &&
+ (config_metapartition_files &
+ IMAP_ENUM_METAPARTITION_FILES_HEADER)) {
+ strlcpy(fnamebuf, mpath, sizeof(fnamebuf));
+ } else {
+ strlcpy(fnamebuf, path, sizeof(fnamebuf));
+ }
+ strlcat(fnamebuf, FNAME_HEADER, sizeof(fnamebuf));
+ if (stat(fnamebuf, &sbuf)) return 0;
+ if ((sbuf.st_mtime == 0) || (sbuf.st_mtime > drock->delete_mark))
+ return(0);
+
+ /* Add this mailbox to list of mailboxes to delete */
+ node = xmalloc(sizeof(struct delete_node));
+ node->next = NULL;
+ node->name = xstrdup(name);
+
+ if (drock->tail) {
+ drock->tail->next = node;
+ drock->tail = node;
+ } else {
+ drock->head = drock->tail = node;
+ }
+ return(0);
+}
+
int main(int argc, char *argv[])
{
extern char *optarg;
- int opt, r = 0, expire_days = 0, expunge_days = 0;
+ int opt, r = 0, expire_days = 0, expunge_days = 0, delete_days = 0;
char *alt_config = NULL;
char buf[100];
struct hash_table expire_table;
struct expire_rock erock;
+ struct delete_rock drock;
+ const char *delete_hierachy;
if (geteuid() == 0) fatal("must run as the Cyrus user", EC_USAGE);
/* zero the expire_rock */
memset(&erock, 0, sizeof(erock));
+ memset(&drock, 0, sizeof(drock));
- while ((opt = getopt(argc, argv, "C:E:X:v")) != EOF) {
+ while ((opt = getopt(argc, argv, "C:D:E:X:v")) != EOF) {
switch (opt) {
case 'C': /* alt config file */
alt_config = optarg;
break;
+ case 'D':
+ if (delete_days) usage();
+ delete_days = atoi(optarg);
+ break;
+
case 'E':
if (expire_days) usage();
expire_days = atoi(optarg);
@@ -298,6 +376,7 @@
case 'v':
erock.verbose++;
+ drock.verbose++;
break;
default:
@@ -354,6 +433,44 @@
erock.deleted, erock.messages, erock.mailboxes);
}
+ if (mboxlist_delayed_delete_isenabled() &&
+ (delete_hierachy = config_getstring(IMAPOPT_DELETE_HIERACHY))) {
+ struct delete_node *node;
+ int count = 0;
+
+ if (drock.verbose) {
+ fprintf(stderr,
+ "Removing deleted mailboxes older than %d days\n",
+ delete_days);
+ }
+
+ strlcpy(drock.prefix, delete_hierachy, sizeof(drock.prefix));
+ strlcat(drock.prefix, ".", sizeof(drock.prefix));
+ drock.prefixlen = strlen(drock.prefix);
+ drock.delete_mark = time(0) - (delete_days * 60 * 60 * 24);
+ drock.head = NULL;
+ drock.tail = NULL;
+
+ mboxlist_findall(NULL, buf, 1, 0, 0, &delete, &drock);
+
+ for (node = drock.head ; node ; node = node->next) {
+ if (drock.verbose) {
+ fprintf(stderr, "Removing: %s\n", node->name);
+ }
+ r = mboxlist_deletemailbox(node->name, 1, NULL, NULL, 0, 0, 0);
+ count++;
+ }
+
+ if (drock.verbose) {
+ if (count != 1) {
+ fprintf(stderr, "Removed %d deleted mailboxes\n", count);
+ } else {
+ fprintf(stderr, "Removed 1 deleted mailbox\n");
+ }
+ }
+ syslog(LOG_NOTICE, "Removed %d deleted mailboxes", count);
+ }
+
/* purge deliver.db entries of expired messages */
r = duplicate_prune(expire_days, &expire_table);
diff -ur cyrus-imapd-2.3.8.orig/imap/imapd.c cyrus-imapd-2.3.8/imap/imapd.c
--- cyrus-imapd-2.3.8.orig/imap/imapd.c 2007-02-05 13:49:55.000000000 -0500
+++ cyrus-imapd-2.3.8/imap/imapd.c 2007-07-23 09:08:38.000000000 -0400
@@ -4981,9 +4981,19 @@
{
int r;
- r = mboxlist_deletemailbox(name, imapd_userisadmin,
- imapd_userid, imapd_authstate,
- 0, 0, 0);
+ if (!mboxlist_delayed_delete_isenabled()) {
+ r = mboxlist_deletemailbox(name, imapd_userisadmin,
+ imapd_userid, imapd_authstate,
+ 0, 0, 0);
+ } else if (imapd_userisadmin && mboxlist_in_delete_hierachy(name)) {
+ r = mboxlist_deletemailbox(name, imapd_userisadmin,
+ imapd_userid, imapd_authstate,
+ 0, 0, 0);
+ } else {
+ r = mboxlist_delayed_deletemailbox(name, imapd_userisadmin,
+ imapd_userid, imapd_authstate,
+ 0, 0, 0);
+ }
if (!r) sync_log_mailbox(name);
@@ -5063,9 +5073,20 @@
if (config_virtdomains && (p = strchr(mailboxname, '!')))
domainlen = p - mailboxname + 1;
- r = mboxlist_deletemailbox(mailboxname, imapd_userisadmin,
- imapd_userid, imapd_authstate, 1-force,
- localonly, 0);
+ if (localonly || !mboxlist_delayed_delete_isenabled()) {
+ r = mboxlist_deletemailbox(mailboxname, imapd_userisadmin,
+ imapd_userid, imapd_authstate,
+ 1-force, localonly, 0);
+ } else if (imapd_userisadmin &&
+ mboxlist_in_delete_hierachy(mailboxname)) {
+ r = mboxlist_deletemailbox(mailboxname, imapd_userisadmin,
+ imapd_userid, imapd_authstate,
+ 0 /* checkacl */, localonly, 0);
+ } else {
+ r = mboxlist_delayed_deletemailbox(mailboxname, imapd_userisadmin,
+ imapd_userid, imapd_authstate,
+ 1-force, localonly, 0);
+ }
}
/* was it a top-level user mailbox? */
@@ -9263,6 +9284,12 @@
else
strlcpy(mboxname, lastname, sizeof(mboxname));
+ /* Suppress DELETED hierachy unless admin */
+ if (!imapd_userisadmin &&
+ mboxlist_delayed_delete_isenabled() &&
+ mboxlist_in_delete_hierachy(mboxname))
+ return;
+
/* Look it up */
nonexistent = mboxlist_detail(mboxname, &mbtype,
NULL, NULL, NULL, NULL, NULL);
diff -ur cyrus-imapd-2.3.8.orig/imap/mboxlist.c
cyrus-imapd-2.3.8/imap/mboxlist.c
--- cyrus-imapd-2.3.8.orig/imap/mboxlist.c 2007-02-05 13:41:47.000000000
-0500
+++ cyrus-imapd-2.3.8/imap/mboxlist.c 2007-07-23 09:11:16.000000000 -0400
@@ -82,6 +82,7 @@
#include "mboxlist.h"
#include "quota.h"
+#include "sync_log.h"
#define DB config_mboxlist_db
#define SUBDB config_subscription_db
@@ -875,6 +876,103 @@
}
/*
+ * Delayed Delete a mailbox: translate delete into rename
+ *
+ * XXX local_only?
+ */
+int
+mboxlist_delayed_deletemailbox(const char *name, int isadmin, char *userid,
+ struct auth_state *auth_state, int checkacl,
+ int local_only, int force)
+{
+ char newname[MAX_MAILBOX_PATH+1];
+ char *path, *mpath;
+ char *acl;
+ char *partition;
+ int r;
+ long access;
+ struct mailbox mailbox;
+ int deletequotaroot = 0;
+ struct txn *tid = NULL;
+ int isremote = 0;
+ int mbtype;
+ const char *p;
+ const char *delete_hierachy = config_getstring(IMAPOPT_DELETE_HIERACHY);
+ int domainlen = 0;
+ struct timeval tv;
+
+ if(!isadmin && force) return IMAP_PERMISSION_DENIED;
+
+ /* Check for request to delete a user:
+ user.<x> with no dots after it */
+ if ((p = mboxname_isusermailbox(name, 1))) {
+ /* Can't DELETE INBOX (your own inbox) */
+ if (userid) {
+ int len = config_virtdomains ?
+ strcspn(userid, "@") : strlen(userid);
+ if ((len == strlen(p)) && !strncmp(p, userid, len)) {
+ return(IMAP_MAILBOX_NOTSUPPORTED);
+ }
+ }
+
+ /* Only admins may delete user */
+ if (!isadmin) return(IMAP_PERMISSION_DENIED);
+ }
+
+ do {
+ r = mboxlist_mylookup(name, &mbtype,
+ &path, &mpath, &partition, &acl, NULL, 1);
+ } while (r == IMAP_AGAIN);
+
+ if (r) return(r);
+
+ isremote = mbtype & MBTYPE_REMOTE;
+
+ /* are we reserved? (but for remote mailboxes this is okay, since
+ * we don't touch their data files at all) */
+ if(!isremote && (mbtype & MBTYPE_RESERVE) && !force) {
+ return(IMAP_MAILBOX_RESERVED);
+ }
+
+ /* check if user has Delete right (we've already excluded non-admins
+ * from deleting a user mailbox) */
+ if (checkacl) {
+ access = cyrus_acl_myrights(auth_state, acl);
+ if(!(access & ACL_DELETEMBOX)) {
+ /* User has admin rights over their own mailbox namespace */
+ if (mboxname_userownsmailbox(userid, name) &&
+ (config_implicitrights & ACL_ADMIN)) {
+ isadmin = 1;
+ }
+
+ /* Lie about error if privacy demands */
+ r = (isadmin || (access & ACL_LOOKUP)) ?
+ IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
+ return(r);
+ }
+ }
+
+ if (config_virtdomains && (p = strchr(name, '!')))
+ domainlen = p - name + 1;
+
+ gettimeofday( &tv, NULL );
+
+ if (domainlen && domainlen < sizeof(newname))
+ strncpy(newname, name, domainlen);
+ snprintf(newname+domainlen, sizeof(newname)-domainlen, "%s.%s.%X",
+ delete_hierachy, name+domainlen, tv.tv_sec);
+
+ /* Get mboxlist_renamemailbox to do the hard work. No ACL checks needed */
+ r = mboxlist_renamemailbox((char *)name, newname, partition,
+ 1 /* isadmin */, userid,
+ auth_state, force);
+
+ /* don't forget to log the rename! */
+ sync_log_mailbox_double((char *)name, newname);
+ return r;
+}
+
+/*
* Delete a mailbox.
* Deleting the mailbox user.FOO may only be performed by an admin.
*
@@ -3261,3 +3359,42 @@
return DB->abort(mbdb, tid);
}
+
+int
+mboxlist_delayed_delete_isenabled(void)
+{
+ static int defined = 0;
+ static enum enum_value config_delete_mode;
+
+ if (!defined) {
+ defined = 1;
+ config_delete_mode = config_getenum(IMAPOPT_DELETE_MODE);
+ }
+
+ return(config_delete_mode == IMAP_ENUM_DELETE_MODE_DELAYED);
+}
+
+int mboxlist_in_delete_hierachy(const char *mailboxname)
+{
+ static int defined = 0;
+ static const char *delete_hierachy = NULL;
+ static int delete_hierachy_len = 0;
+ int domainlen = 0;
+ char *p;
+
+ if (!defined) {
+ defined = 1;
+ delete_hierachy = config_getstring(IMAPOPT_DELETE_HIERACHY);
+ if (delete_hierachy)
+ delete_hierachy_len = strlen(delete_hierachy);
+ }
+
+ if (!delete_hierachy || !mboxlist_delayed_delete_isenabled())
+ return(0);
+
+ if (config_virtdomains && (p = strchr(mailboxname, '!')))
+ domainlen = p - mailboxname + 1;
+
+ return ((!strncmp(mailboxname + domainlen, delete_hierachy,
delete_hierachy_len) &&
+ mailboxname[domainlen + delete_hierachy_len] == '.') ? 1 : 0);
+}
diff -ur cyrus-imapd-2.3.8.orig/imap/mboxlist.h
cyrus-imapd-2.3.8/imap/mboxlist.h
--- cyrus-imapd-2.3.8.orig/imap/mboxlist.h 2006-11-30 12:11:19.000000000
-0500
+++ cyrus-imapd-2.3.8/imap/mboxlist.h 2007-07-23 09:08:38.000000000 -0400
@@ -118,6 +118,12 @@
struct auth_state *auth_state,
int localonly, int forceuser, int dbonly);
+/* delated delete */
+/* Translate delete into rename */
+int
+mboxlist_delayed_deletemailbox(const char *name, int isadmin, char *userid,
+ struct auth_state *auth_state, int checkacl,
+ int local_only, int force);
/* Delete a mailbox. */
/* setting local_only disables any communication with the mupdate server
* and deletes the mailbox from the filesystem regardless of if it is
@@ -204,4 +210,6 @@
int mboxlist_commit(struct txn *tid);
int mboxlist_abort(struct txn *tid);
+int mboxlist_delayed_delete_isenabled(void);
+int mboxlist_in_delete_hierachy(const char *mailboxname);
#endif
diff -ur cyrus-imapd-2.3.8.orig/lib/imapoptions
cyrus-imapd-2.3.8/lib/imapoptions
--- cyrus-imapd-2.3.8.orig/lib/imapoptions 2007-02-07 13:58:07.000000000
-0500
+++ cyrus-imapd-2.3.8/lib/imapoptions 2007-07-23 09:08:38.000000000 -0400
@@ -197,6 +197,16 @@
{ "defaultpartition", "default", STRING }
/* The partition name used by default for new mailboxes. */
+{ "delete_hierachy", "DELETED", STRING }
+/* Location for deleted mailboxes, if "delete_mode" set to be "delayed" */
+
+{ "delete_mode", "immediate", ENUM("immediate", "delayed") }
+/* The manner in which mailboxes are deleted. "Immediate" mode is the
+ default behavior in which mailboxes are removed immediately. In
+ "Delayed" mode, mailboxes are renamed to a special hiearchy defined
+ by the "Delete_hierachy" option to be removed later by cyr_expire.
+*/
+
{ "deleteright", "c", STRING }
/* Deprecated - only used for backwards compatibility with existing
installations. Lists the old RFC 2086 right which was used to
diff -ur cyrus-imapd-2.3.8.orig/man/cyr_expire.8
cyrus-imapd-2.3.8/man/cyr_expire.8
--- cyrus-imapd-2.3.8.orig/man/cyr_expire.8 2006-11-30 12:11:23.000000000
-0500
+++ cyrus-imapd-2.3.8/man/cyr_expire.8 2007-07-23 09:08:38.000000000 -0400
@@ -48,6 +48,9 @@
.B \-C
.I config-file
]
+[
+.BI \-D " delete-days"
+]
.BI \-E " expire-days"
[
.BI \-X " expunge-days"
@@ -84,6 +87,11 @@
.BI \-C " config-file"
Read configuration options from \fIconfig-file\fR.
.TP
+\fB\-D \fIdelete-days\fR
+Remove previously deleted mailboxes older than \fIdelete-days\fR
+(when using the "delayed" delete mode). The default is 0 (zero)
+days, which will delete \fBall\fR previously deleted mailboxes.
+.TP
\fB\-E \fIexpire-days\fR
Prune the duplicate database of entries older than \fIexpire-days\fR. This
value is only used for entries which do not have a corresponding