HIDDEN int mailbox_rename_copy(struct mailbox *oldmailbox,
                        const char *newname,
                        const char *newpartition,
                        unsigned uidvalidity,
                        int ignorequota, int silent,
                        struct mailbox **newmailboxptr)
{
    int r;
    struct mailbox *newmailbox = NULL;
    struct conversations_state *oldcstate = NULL;
    struct conversations_state *newcstate = NULL;
    char *newquotaroot = NULL;
    char *newuniqueid = NULL;

    assert(mailbox_index_islocked(oldmailbox, 1));
    assert(mailbox_mbtype(oldmailbox) & MBTYPE_LEGACY_DIRS);

    /* we can't rename back from a deleted mailbox, because the conversations
     * information will be wrong.  Ideally we might re-calculate, but for now
     * we just throw a big fat error */
    if (config_getswitch(IMAPOPT_CONVERSATIONS) &&
        mboxname_isdeletedmailbox(mailbox_name(oldmailbox), NULL)) {
        syslog(LOG_ERR, "can't rename a deleted mailbox %s", mailbox_name(oldmailbox));
        return IMAP_MAILBOX_BADNAME;
    }

    /* create uidvalidity if not explicitly requested */
    if (!uidvalidity)
        uidvalidity = mboxname_nextuidvalidity(newname, oldmailbox->i.uidvalidity);

    modseq_t highestmodseq = silent ? oldmailbox->i.highestmodseq : 0;

    /* Create new mailbox */
    r = mailbox_create(newname, mailbox_mbtype(oldmailbox), newpartition,
                       mailbox_acl(oldmailbox), mailbox_uniqueid(oldmailbox),
                       oldmailbox->i.options, uidvalidity,
                       oldmailbox->i.createdmodseq,
                       highestmodseq, &newmailbox);

    if (r) return r;

    /* Check quota if necessary */
    if (!ignorequota && newmailbox->h.quotaroot &&
        strcmpsafe(oldmailbox->h.quotaroot, newmailbox->h.quotaroot)) {

        quota_t usage[QUOTA_NUMRESOURCES];
        mailbox_get_usage(oldmailbox, usage);
        r = mailbox_quota_check(newmailbox, usage);
        /* then we abort - no space to rename */
        if (r)
            goto fail;
    }
    newquotaroot = xstrdupnull(newmailbox->h.quotaroot);

    r = mailbox_copy_files(oldmailbox, newpartition,
                           newname, mailbox_mbtype(newmailbox) & MBTYPE_LEGACY_DIRS ?
                           NULL : mailbox_uniqueid(newmailbox));
    if (r) goto fail;

    /* Re-open index file  */
    r = mailbox_open_index(newmailbox);
    if (r) goto fail;

    /* cyrus.header has been copied with old uniqueid.
       make a copy of new uniqueid so we can reset it */
    newuniqueid = xstrdup(mailbox_uniqueid(newmailbox));

    /* Re-lock index */
    r = mailbox_lock_index_internal(newmailbox, LOCK_EXCLUSIVE);

    /* Reset new uniqueid */
    free(newmailbox->h.uniqueid);
    newmailbox->h.uniqueid = xstrdup(newuniqueid);
    newmailbox->header_dirty = 1;

    /* update mailbox annotations if necessary */
    r = annotate_rename_mailbox(oldmailbox, newmailbox);
    if (r) goto fail;

    /* mark the "used" back to zero, so it updates the new quota! */
    mailbox_set_quotaroot(newmailbox, newquotaroot);
    mailbox_quota_dirty(newmailbox);
    memset(newmailbox->quota_previously_used, 0, sizeof(newmailbox->quota_previously_used));
    /* except this one... because we've counted it when we created the folder */
    newmailbox->quota_previously_used[QUOTA_NUMFOLDERS] = 1;

    /* re-set the UIDVALIDITY, it will have been the old one in the index header */
    mailbox_index_dirty(newmailbox);
    newmailbox->i.uidvalidity = uidvalidity;
    /* unless on a replica, bump the modseq too */
    if (!silent) mailbox_modseq_dirty(newmailbox);

    /* NOTE: in the case of renaming a user to another user, we
     * don't rename the conversations DB - instead we re-create
     * the records in the target user.  Sorry, was too complex
     * otherwise handling all the special cases */
    if (mailbox_has_conversations(oldmailbox)) {
        oldcstate = mailbox_get_cstate(oldmailbox);
        assert(oldcstate);
    }

    if (mailbox_has_conversations(newmailbox)) {
        newcstate = mailbox_get_cstate(newmailbox);
        assert(newcstate);
    }

    if (oldcstate && newcstate && !strcmp(oldcstate->path, newcstate->path)) {
        /* we can just rename within the same user */
        if (oldcstate->folders_byname) {
            r = conversations_rename_folder(oldcstate, mailbox_name(oldmailbox), newname);
        }
    }
    else {
        /* have to handle each one separately */
        if (newcstate)
            r = mailbox_add_conversations(newmailbox, /*silent*/0);
        if (oldcstate)
            r = mailbox_delete_conversations(oldmailbox);
    }
    if (r) goto fail;

    /* commit the index changes */
    r = mailbox_commit(newmailbox);
    if (r) goto fail;

    if (config_auditlog)
        syslog(LOG_NOTICE, "auditlog: rename sessionid=<%s> "
                           "oldmailbox=<%s> newmailbox=<%s> uniqueid=<%s>",
                           session_id(),
                           mailbox_name(oldmailbox), newname, mailbox_uniqueid(newmailbox));

    if (newmailboxptr) *newmailboxptr = newmailbox;
    else mailbox_close(&newmailbox);
    free(newquotaroot);
    free(newuniqueid);

    return 0;

fail:
    /* first unlock so we don't need to write anything new down */
    mailbox_unlock_index(newmailbox, NULL);
    /* then remove all the files */
    mailbox_delete_cleanup(NULL, mailbox_partition(newmailbox), mailbox_name(newmailbox),
                           (mailbox_mbtype(newmailbox) & MBTYPE_LEGACY_DIRS) ?
                           NULL : mailbox_uniqueid(newmailbox));
    /* and finally, abort */
    mailbox_abort(newmailbox);
    mailbox_close(&newmailbox);
    free(newquotaroot);
    free(newuniqueid);

    return r;
}
HIDDEN int mailbox_rename_copy(struct mailbox *oldmailbox,
                        const char *newname,
                        const char *newpartition,
                        unsigned uidvalidity,
                        int ignorequota, int silent,
                        struct mailbox **newmailboxptr)
{
    int r;
    struct mailbox *newmailbox = NULL;
    struct conversations_state *oldcstate = NULL;
    struct conversations_state *newcstate = NULL;
    char *newquotaroot = NULL;
    char *newuniqueid = NULL;

    assert(mailbox_index_islocked(oldmailbox, 1));
    assert(mailbox_mbtype(oldmailbox) & MBTYPE_LEGACY_DIRS);

     
    if (config_getswitch(IMAPOPT_CONVERSATIONS) &&
        mboxname_isdeletedmailbox(mailbox_name(oldmailbox), NULL)) {
        syslog(LOG_ERR, "can't rename a deleted mailbox %s", mailbox_name(oldmailbox));
        return IMAP_MAILBOX_BADNAME;
    }

     
    if (!uidvalidity)
        uidvalidity = mboxname_nextuidvalidity(newname, oldmailbox->i.uidvalidity);

    modseq_t highestmodseq = silent ? oldmailbox->i.highestmodseq : 0;

     
    r = mailbox_create(newname, mailbox_mbtype(oldmailbox), newpartition,
                       mailbox_acl(oldmailbox), mailbox_uniqueid(oldmailbox),
                       oldmailbox->i.options, uidvalidity,
                       oldmailbox->i.createdmodseq,
                       highestmodseq, &newmailbox);

    if (r) return r;

     
    if (!ignorequota && newmailbox->h.quotaroot &&
        strcmpsafe(oldmailbox->h.quotaroot, newmailbox->h.quotaroot)) {

        quota_t usage[QUOTA_NUMRESOURCES];
        mailbox_get_usage(oldmailbox, usage);
        r = mailbox_quota_check(newmailbox, usage);
         
        if (r)
            goto fail;
    }
    newquotaroot = xstrdupnull(newmailbox->h.quotaroot);

    r = mailbox_copy_files(oldmailbox, newpartition,
                           newname, mailbox_mbtype(newmailbox) & MBTYPE_LEGACY_DIRS ?
                           NULL : mailbox_uniqueid(newmailbox));
    if (r) goto fail;

     
    r = mailbox_open_index(newmailbox);
    if (r) goto fail;

     
    newuniqueid = xstrdup(mailbox_uniqueid(newmailbox));

     
    r = mailbox_lock_index_internal(newmailbox, LOCK_EXCLUSIVE);

     
    free(newmailbox->h.uniqueid);
    newmailbox->h.uniqueid = xstrdup(newuniqueid);
    newmailbox->header_dirty = 1;

     
    r = annotate_rename_mailbox(oldmailbox, newmailbox);
    if (r) goto fail;

     
    mailbox_set_quotaroot(newmailbox, newquotaroot);
    mailbox_quota_dirty(newmailbox);
    memset(newmailbox->quota_previously_used, 0, sizeof(newmailbox->quota_previously_used));
     
    newmailbox->quota_previously_used[QUOTA_NUMFOLDERS] = 1;

     
    mailbox_index_dirty(newmailbox);
    newmailbox->i.uidvalidity = uidvalidity;
     
    if (!silent) mailbox_modseq_dirty(newmailbox);

     
    if (mailbox_has_conversations(oldmailbox)) {
        oldcstate = mailbox_get_cstate(oldmailbox);
        assert(oldcstate);
    }

    if (mailbox_has_conversations(newmailbox)) {
        newcstate = mailbox_get_cstate(newmailbox);
        assert(newcstate);
    }

    if (oldcstate && newcstate && !strcmp(oldcstate->path, newcstate->path)) {
         
        if (oldcstate->folders_byname) {
            r = conversations_rename_folder(oldcstate, mailbox_name(oldmailbox), newname);
        }
    }
    else {
         
        if (newcstate)
            r = mailbox_add_conversations(newmailbox,  0);
        if (oldcstate)
            r = mailbox_delete_conversations(oldmailbox);
    }
    if (r) goto fail;

     
    r = mailbox_commit(newmailbox);
    if (r) goto fail;

    if (config_auditlog)
        syslog(LOG_NOTICE, "auditlog: rename sessionid=<%s> "
                           "oldmailbox=<%s> newmailbox=<%s> uniqueid=<%s>",
                           session_id(),
                           mailbox_name(oldmailbox), newname, mailbox_uniqueid(newmailbox));

    if (newmailboxptr) *newmailboxptr = newmailbox;
    else mailbox_close(&newmailbox);
    free(newquotaroot);
    free(newuniqueid);

    return 0;

fail:
     
    mailbox_unlock_index(newmailbox, NULL);
     
    mailbox_delete_cleanup(NULL, mailbox_partition(newmailbox), mailbox_name(newmailbox),
                           (mailbox_mbtype(newmailbox) & MBTYPE_LEGACY_DIRS) ?
                           NULL : mailbox_uniqueid(newmailbox));
     
    mailbox_abort(newmailbox);
    mailbox_close(&newmailbox);
    free(newquotaroot);
    free(newuniqueid);

    return r;
}