diff -Naur cyrus-imapd-2.3.8/imap/ctl_cyrusdb.c cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/ctl_cyrusdb.c --- cyrus-imapd-2.3.8/imap/ctl_cyrusdb.c 2006-11-30 19:11:17.000000000 +0200 +++ cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/ctl_cyrusdb.c 2007-02-21 16:34:33.000000000 +0200 @@ -133,7 +133,7 @@ /* if it is MBTYPE_RESERVED, unset it & call mboxlist_delete */ if(!r && (mbtype & MBTYPE_RESERVE)) { if(!r) { - r = mboxlist_deletemailbox(name, 1, NULL, NULL, 0, 0, 1); + r = mboxlist_deletemailbox(name, 1, NULL, NULL, 0, 0, 1, 1); if(r) { /* log the error */ syslog(LOG_ERR, diff -Naur cyrus-imapd-2.3.8/imap/ctl_mboxlist.c cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/ctl_mboxlist.c --- cyrus-imapd-2.3.8/imap/ctl_mboxlist.c 2007-02-05 20:41:46.000000000 +0200 +++ cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/ctl_mboxlist.c 2007-02-21 16:34:48.000000000 +0200 @@ -461,7 +461,7 @@ wipe_head = wipe_head->next; - ret = mboxlist_deletemailbox(me->mailbox, 1, "", NULL, 0, 1, 1); + ret = mboxlist_deletemailbox(me->mailbox, 1, "", NULL, 0, 1, 1, 1); if(!ret) sync_log_mailbox(me->mailbox); if(ret) { fprintf(stderr, "couldn't delete defunct mailbox %s\n", diff -Naur cyrus-imapd-2.3.8/imap/imapd.c cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/imapd.c --- cyrus-imapd-2.3.8/imap/imapd.c 2007-02-05 20:49:55.000000000 +0200 +++ cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/imapd.c 2007-02-21 16:34:58.000000000 +0200 @@ -4983,7 +4983,7 @@ r = mboxlist_deletemailbox(name, imapd_userisadmin, imapd_userid, imapd_authstate, - 0, 0, 0); + 0, 0, 0, 1); if (!r) sync_log_mailbox(name); @@ -5007,6 +5007,12 @@ char *p; int domainlen = 0; int sync_lockfd = (-1); + int keepQuota = 1; + + if(name && *name == '+') { + keepQuota = 0; + name++; + } r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name, imapd_userid, mailboxname); @@ -5065,7 +5071,7 @@ r = mboxlist_deletemailbox(mailboxname, imapd_userisadmin, imapd_userid, imapd_authstate, 1-force, - localonly, 0); + localonly, 0, keepQuota); } /* was it a top-level user mailbox? */ @@ -6423,6 +6429,7 @@ { int newquota = -1; int badresource = 0; + int rmquota = 0; int c; int force = 0; static struct buf arg; @@ -6439,7 +6446,8 @@ if (c != ')' || arg.s[0] != '\0') { for (;;) { if (c != ' ') goto badlist; - if (strcasecmp(arg.s, "storage") != 0) badresource = 1; + if (strcasecmp(arg.s, "remove") == 0) rmquota = 1; + else if (strcasecmp(arg.s, "storage") != 0) badresource = 1; c = getword(imapd_in, &arg); if (c != ' ' && c != ')') goto badlist; if (arg.s[0] == '\0') goto badlist; @@ -6508,7 +6516,10 @@ /* local mailbox */ if (!r || (r == IMAP_MAILBOX_NONEXISTENT)) { - r = mboxlist_setquota(mailboxname, newquota, force); + if(!rmquota) + r = mboxlist_setquota(mailboxname, newquota, force); + else + r = mboxlist_unsetquota(mailboxname); } imapd_check(NULL, 0, 0); @@ -8221,7 +8232,7 @@ /* note also that we need to remember to let proxyadmins do this */ r = mboxlist_deletemailbox(mailboxname, imapd_userisadmin || imapd_userisproxyadmin, - imapd_userid, imapd_authstate, 0, 1, 0); + imapd_userid, imapd_authstate, 0, 1, 0, 1); if(r) syslog(LOG_ERR, "Could not delete local mailbox during move of %s", mailboxname); diff -Naur cyrus-imapd-2.3.8/imap/mailbox.c cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/mailbox.c --- cyrus-imapd-2.3.8/imap/mailbox.c 2007-02-05 20:41:47.000000000 +0200 +++ cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/mailbox.c 2007-02-21 16:35:10.000000000 +0200 @@ -2745,27 +2745,7 @@ seen_delete_mailbox(mailbox); - if (delete_quota_root && !rquota) { - quota_delete(&mailbox->quota, &tid); - free(mailbox->quota.root); - mailbox->quota.root = NULL; - } else if (!rquota) { - /* Free any quota being used by this mailbox */ - if (mailbox->quota.used >= mailbox->quota_mailbox_used) { - mailbox->quota.used -= mailbox->quota_mailbox_used; - } - else { - mailbox->quota.used = 0; - } - r = quota_write(&mailbox->quota, &tid); - if (r) { - syslog(LOG_ERR, - "LOSTQUOTA: unable to record free of " UQUOTA_T_FMT " bytes in quota %s", - mailbox->quota_mailbox_used, mailbox->quota.root); - } - else - quota_commit(&tid); - } + mailbox_updatequota(mailbox,NULL); /* Flush data (message file) directory */ mailbox_delete_files(mailbox->path); @@ -3392,3 +3372,48 @@ if (*p == '.') *p = '/'; } } + +/* This function is used to update the quota. Can be used to replace + * identical parts of the code, and can be quite handy some times + * The tid is used in order to make possible to make the quota update + * being a part of a bigger transaction to the quota db */ +int mailbox_updatequota(struct mailbox *mailbox, struct txn **tid) +{ + int r = 0, havetid = 0; + struct txn **ltid = NULL; + + if(tid) { + ltid = tid; + havetid = 1; + } + /* Ensure that we are locked */ + if(!mailbox->header_lock_count) return IMAP_INTERNAL; + + + if(mailbox->quota.root) { + r = quota_read(&mailbox->quota, ltid, 1); + if( r == 0 ) { + if (mailbox->quota.used >= mailbox->quota_mailbox_used) { + mailbox->quota.used -= mailbox->quota_mailbox_used; + } + else { + mailbox->quota.used = 0; + } + r = quota_write(&mailbox->quota, ltid); + if (r) { + syslog(LOG_ERR, + "LOSTQUOTA: unable to record free of %lu bytes in quota %s", + mailbox->quota_mailbox_used, mailbox->quota.root); + } + else if(!havetid) + quota_commit(tid); + } + /* It is not a big mistake not to have quota .. just remove from the mailbox */ + else if ( r == IMAP_QUOTAROOT_NONEXISTENT) { + free(mailbox->quota.root); + r = 0; + } + } + return r; +} + diff -Naur cyrus-imapd-2.3.8/imap/mailbox.h cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/mailbox.h --- cyrus-imapd-2.3.8/imap/mailbox.h 2006-11-30 19:11:19.000000000 +0200 +++ cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/mailbox.h 2007-02-21 16:35:20.000000000 +0200 @@ -364,6 +364,8 @@ struct mailbox *mailboxp); extern int mailbox_delete(struct mailbox *mailbox, int delete_quota_root); +extern int mailbox_updatequota(struct mailbox *mailbox, struct txn **tid); + extern int mailbox_rename_copy(struct mailbox *oldmailbox, const char *newname, char *newpartition, bit32 *olduidvalidityp, bit32 *newuidvalidityp, diff -Naur cyrus-imapd-2.3.8/imap/mboxlist.c cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/mboxlist.c --- cyrus-imapd-2.3.8/imap/mboxlist.c 2007-02-05 20:41:47.000000000 +0200 +++ cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/mboxlist.c 2007-02-21 16:35:29.000000000 +0200 @@ -95,6 +95,12 @@ static int mboxlist_opensubs(); static void mboxlist_closesubs(); +static int child_cb(char *name, + int matchlen __attribute__((unused)), + int maycreate __attribute__((unused)), + void *rock); + + static int mboxlist_rmquota(const char *name, int matchlen, int maycreate, void *rock); static int mboxlist_changequota(const char *name, int matchlen, int maycreate, @@ -102,6 +108,7 @@ struct change_rock { struct quota *quota; + struct quota *oldquota; struct txn **tid; }; @@ -889,9 +896,9 @@ */ int mboxlist_deletemailbox(const char *name, int isadmin, char *userid, struct auth_state *auth_state, int checkacl, - int local_only, int force) + int local_only, int force, int keepQuota) { - int r; + int r, has_children = 0; char *acl; long access; struct mailbox mailbox; @@ -902,6 +909,7 @@ int mbtype; const char *p; mupdate_handle *mupdate_h = NULL; + char *quotaroot = NULL; if(!isadmin && force) return IMAP_PERMISSION_DENIED; @@ -1014,15 +1022,47 @@ if ((r && !force) || isremote) goto done; - if (!r || force) r = mailbox_delete(&mailbox, deletequotaroot); + if (!r || force) { + /* first we have to keep the previous quota root in order to delete it */ + if(mailbox.quota.root) + quotaroot = xstrdup(mailbox.quota.root); + r = mailbox_delete(&mailbox, deletequotaroot); + } + /* * See if we have to remove mailbox's quota root */ - if (!r && mailbox.quota.root != NULL) { - /* xxx look for any other mailboxes in this quotaroot */ + if (!r && quotaroot != NULL) { + /* xxx look for any other mailboxes in this quotaroot */ + /* If we have not asked to remove the quota (default behaviour), we check + * whether there are any subfolders beneeth the quota root. If there aren't + * any subfolders the reasonable thing is to delete the quota */ + if(keepQuota) { + char pattern[MAX_MAILBOX_PATH+1]; + strlcpy(pattern, quotaroot, sizeof(pattern)); + if (config_virtdomains && name[strlen(name)-1] == '!') { + strlcat(pattern, "*", sizeof(pattern)); + } + else { + strlcat(pattern, ".*", sizeof(pattern)); + } + /* find if there are subfolders. Then we want to + * keep the existing quota */ + mboxlist_findall(NULL, pattern, isadmin, userid, + auth_state, child_cb, (void *) &has_children); + + if(!has_children) + if(!mboxlist_mylookup(quotaroot, NULL, NULL, NULL, NULL, NULL, 0, 1)) + has_children = 1; + } + /* If we want to remove the quota explicitely or the quota root folder has no subfolders + * we execute the rmquota patch */ + if(!keepQuota || !has_children ) + mboxlist_unsetquota(quotaroot); + free(quotaroot); } - + done: if(r && tid && !force) { /* Abort the transaction if it is still in progress */ @@ -2483,6 +2523,7 @@ if (r) return r; crock.quota = "a; + crock.oldquota = NULL; crock.tid = &tid; /* top level mailbox */ if(have_mailbox) @@ -2501,15 +2542,19 @@ */ int mboxlist_unsetquota(const char *root) { + char newquota[MAX_MAILBOX_PATH+1]; char pattern[MAX_MAILBOX_PATH+1]; struct quota quota; - int r=0; + struct change_rock crock; + int r=0, k=0; if (!root[0] || root[0] == '.' || strchr(root, '/') || strchr(root, '*') || strchr(root, '%') || strchr(root, '?')) { return IMAP_MAILBOX_BADNAME; } + crock.tid=NULL; + quota.root = (char *) root; r = quota_read("a, NULL, 0); if (r == IMAP_QUOTAROOT_NONEXISTENT) { @@ -2534,9 +2579,47 @@ /* submailboxes - we're using internal names here */ mboxlist_findall(NULL, pattern, 1, 0, 0, mboxlist_rmquota, (void *)root); - r = quota_delete("a, NULL); + r = quota_delete("a, crock.tid); + + /* If we cannot delete the quota then abort the operation */ + if(!r) { + /* quota_findroot performs several checks that we can + * assume that are already done, and don't have to perform + * them again. One of them is that it returns 1 only if + * quotaroot exists. + */ + if(quota_findroot(newquota, sizeof(newquota), root)) { + struct quota rootquota; + rootquota.root = newquota; + k = quota_read(&rootquota, crock.tid, 0); + if (!k) { + crock.quota = &rootquota; + crock.oldquota = "a; + /* top level mailbox */ + k = mboxlist_changequota(root, 0, 0, &crock); + } + /* submailboxes - we're using internal names here */ + if (!k) + k = mboxlist_findall(NULL, pattern, 1, 0, 0, mboxlist_changequota, &crock); + if(!k) + k = quota_write(&rootquota, crock.tid); + + } + else { + /* top level mailbox */ + mboxlist_rmquota(root, 0, 0, (void *)root); + /* submailboxes - we're using internal names here */ + mboxlist_findall(NULL, pattern, 1, 0, 0, mboxlist_rmquota, (void *)root); + } + } + + if(!r && !k) + quota_commit(crock.tid); + else + quota_abort(crock.tid); return r; + } /* @@ -2632,6 +2715,7 @@ struct mailbox mailbox; struct change_rock *crock = (struct change_rock *) rock; struct quota *mboxlist_newquota = crock->quota; + struct quota *mboxlist_oldquota = crock->oldquota; struct txn **tid = crock->tid; assert(rock != NULL); @@ -2649,27 +2733,24 @@ if (r) goto error; if (mailbox.quota.root) { - if (strlen(mailbox.quota.root) >= strlen(mboxlist_newquota->root)) { - /* Part of a child quota root */ - mailbox_close(&mailbox); - return 0; - } - - r = quota_read(&mailbox.quota, tid, 1); - if (r) goto error; - if (mailbox.quota.used >= mailbox.quota_mailbox_used) { - mailbox.quota.used -= mailbox.quota_mailbox_used; - } - else { - mailbox.quota.used = 0; - } - r = quota_write(&mailbox.quota, tid); - if (r) { - syslog(LOG_ERR, - "LOSTQUOTA: unable to record free of " UQUOTA_T_FMT " bytes in quota %s", - mailbox.quota_mailbox_used, mailbox.quota.root); - } - free(mailbox.quota.root); + if(mboxlist_oldquota) { + if (strlen(mailbox.quota.root) > strlen(mboxlist_oldquota->root)) { + /* Part of a child quota root */ + mailbox_close(&mailbox); + return 0; + } + } + else { + if (strlen(mailbox.quota.root) >= strlen(mboxlist_newquota->root)) { + /* Part of a child quota root */ + mailbox_close(&mailbox); + return 0; + } + } + + r = mailbox_updatequota(&mailbox,tid); + if (r) + goto error; } mailbox.quota.root = xstrdup(mboxlist_newquota->root); @@ -2682,6 +2763,11 @@ error: mailbox_close(&mailbox); + syslog(LOG_ERR, "LOSTQUOTA: unable to change quota root for %s to %s: %s. \ + Command aborted. Run reconstruct to make sure mailboxes \ + are in consistent state", + name, mboxlist_newquota->root, error_message(r)); + return 1; error_noclose: syslog(LOG_ERR, "LOSTQUOTA: unable to change quota root for %s to %s: %s", name, mboxlist_newquota->root, error_message(r)); diff -Naur cyrus-imapd-2.3.8/imap/mboxlist.h cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/mboxlist.h --- cyrus-imapd-2.3.8/imap/mboxlist.h 2006-11-30 19:11:19.000000000 +0200 +++ cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/mboxlist.h 2007-02-21 16:35:34.000000000 +0200 @@ -126,7 +126,7 @@ * the planet */ int mboxlist_deletemailbox(const char *name, int isadmin, char *userid, struct auth_state *auth_state, int checkacl, - int local_only, int force); + int local_only, int force, int keepQuota); /* Rename/move a mailbox (hierarchical) */ int mboxlist_renamemailbox(char *oldname, char *newname, char *partition, diff -Naur cyrus-imapd-2.3.8/imap/mupdate.c cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/mupdate.c --- cyrus-imapd-2.3.8/imap/mupdate.c 2007-01-31 16:10:05.000000000 +0200 +++ cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/mupdate.c 2007-02-21 16:35:44.000000000 +0200 @@ -2313,7 +2313,7 @@ remote_boxes.head = r->next; } else if (ret < 0) { /* Local without corresponding remote, delete it */ - mboxlist_deletemailbox(l->mailbox, 1, "", NULL, 0, 0, 0); + mboxlist_deletemailbox(l->mailbox, 1, "", NULL, 0, 0, 0, 1); local_boxes.head = l->next; } else /* (ret > 0) */ { /* Remote without corresponding local, insert it */ @@ -2328,7 +2328,7 @@ if(l && !r) { /* we have more deletes to do */ while(l) { - mboxlist_deletemailbox(l->mailbox, 1, "", NULL, 0, 0, 0); + mboxlist_deletemailbox(l->mailbox, 1, "", NULL, 0, 0, 0, 1); local_boxes.head = l->next; l = local_boxes.head; } diff -Naur cyrus-imapd-2.3.8/imap/nntpd.c cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/nntpd.c --- cyrus-imapd-2.3.8/imap/nntpd.c 2007-02-05 20:49:56.000000000 +0200 +++ cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/nntpd.c 2007-02-21 16:35:53.000000000 +0200 @@ -3358,7 +3358,7 @@ /* XXX should we delete right away, or wait until empty? */ r = mboxlist_deletemailbox(mailboxname, 0, - newsmaster, newsmaster_authstate, 1, 0, 0); + newsmaster, newsmaster_authstate, 1, 0, 0, 1); if (!r) sync_log_mailbox(mailboxname); diff -Naur cyrus-imapd-2.3.8/imap/sync_reset.c cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/sync_reset.c --- cyrus-imapd-2.3.8/imap/sync_reset.c 2006-11-30 19:11:20.000000000 +0200 +++ cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/sync_reset.c 2007-02-22 13:23:41.000000000 +0200 @@ -258,7 +258,7 @@ if (r) goto fail; for (item = list->head ; item ; item = item->next) { - r=mboxlist_deletemailbox(item->name, 1, NULL, sync_authstate, 1, 0, 0); + r=mboxlist_deletemailbox(item->name, 1, NULL, sync_authstate, 1, 0, 0, 1); if (r) goto fail; } @@ -274,7 +274,7 @@ if (r) goto fail; for (item = list->head ; item ; item = item->next) { - r=mboxlist_deletemailbox(item->name, 1, NULL, sync_authstate, 1, 0, 0); + r=mboxlist_deletemailbox(item->name, 1, NULL, sync_authstate, 1, 0, 0, 1); if (r) goto fail; } @@ -282,7 +282,7 @@ /* Nuke inbox (recursive nuke possible?) */ snprintf(buf, sizeof(buf)-1, "user.%s", user); - r = mboxlist_deletemailbox(buf, 1, "cyrus", sync_authstate, 1, 0, 0); + r = mboxlist_deletemailbox(buf, 1, "cyrus", sync_authstate, 1, 0, 0, 1); if (r && (r != IMAP_MAILBOX_NONEXISTENT)) goto fail; if ((r=user_deletedata(user, sync_userid, sync_authstate, 1))) diff -Naur cyrus-imapd-2.3.8/imap/sync_server.c cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/sync_server.c --- cyrus-imapd-2.3.8/imap/sync_server.c 2006-11-30 19:11:20.000000000 +0200 +++ cyrus-imapd-2.3.8-rmquota+deletemailbox.uncompiled/imap/sync_server.c 2007-02-22 13:24:17.000000000 +0200 @@ -1581,7 +1581,7 @@ for (item = list->head ; item ; item = item->next) { r=mboxlist_deletemailbox(item->name, sync_userisadmin, sync_userid, - sync_authstate, 0, 0, 1); + sync_authstate, 0, 0, 1, 1); if (r) goto fail; } @@ -1591,7 +1591,7 @@ (sync_namespacep->mboxname_tointernal)(sync_namespacep, "INBOX", user, buf); r = mboxlist_deletemailbox(buf, sync_userisadmin, sync_userid, - sync_authstate, 0, 0, 1); + sync_authstate, 0, 0, 1, 1); if (r && (r != IMAP_MAILBOX_NONEXISTENT)) goto fail; if ((r=user_deletedata(user, sync_userid, sync_authstate, 1))) @@ -2526,7 +2526,7 @@ /* Delete with admin priveleges */ r = mboxlist_deletemailbox(name, sync_userisadmin, sync_userid, - sync_authstate, 0, 0, 1); + sync_authstate, 0, 0, 1, 1); if (r) prot_printf(sync_out, "NO Failed to delete %s: %s\r\n",