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

Reply via email to