EXPORTED int mailbox_rewrite_index_record(struct mailbox *mailbox,
                                          struct index_record *record)
{
    int r;
    struct index_record oldrecord;
    int expunge_mode = config_getenum(IMAPOPT_EXPUNGE_MODE);
    int immediate = (expunge_mode == IMAP_ENUM_EXPUNGE_MODE_IMMEDIATE ||
                     expunge_mode == IMAP_ENUM_EXPUNGE_MODE_SEMIDELAYED ||
                     mailbox->i.minor_version < 12);
    int changeflags = 0;

    assert(mailbox_index_islocked(mailbox, 1));
    assert(record->recno > 0 &&
           record->recno <= mailbox->i.num_records);

    r = mailbox_read_index_record(mailbox, record->recno, &oldrecord);
    if (r) {
        xsyslog(LOG_ERR, "IOERROR: re-reading record failed",
                         "mailbox=<%s> record=<%u>",
                         mailbox_name(mailbox), record->uid);
        return r;
    }
    mailbox_read_basecid(mailbox, &oldrecord);

    /* OK, we need to decide how to handle basecid.  Three cases here:
     * 1) basecid is already set - keep it
     * 2) basecid is zero, but the system flag says it should be readable, set it from oldrecord
     * 3) basecid is zero, system flag is zero - keep zero
     * we only need code for case #2
     */
    if (!record->basecid && (record->internal_flags & FLAG_INTERNAL_SPLITCONVERSATION))
        record->basecid = oldrecord.basecid;

    if (oldrecord.internal_flags & FLAG_INTERNAL_EXPUNGED)
        changeflags |= CHANGE_WASEXPUNGED;
    if (oldrecord.internal_flags & FLAG_INTERNAL_UNLINKED)
        changeflags |= CHANGE_WASUNLINKED;

    /* the UID has to match, of course, for it to be the same
     * record.  XXX - test fields like "internaldate", etc here
     * too?  Maybe replication should be more strict about it */
    assert(record->uid == oldrecord.uid);
    assert(message_guid_equal(&oldrecord.guid, &record->guid));

    if (oldrecord.internal_flags & FLAG_INTERNAL_EXPUNGED) {
        /* it is a sin to unexpunge a message.  unexpunge.c copies
         * the data from the old record and appends it with a new
         * UID, which is righteous in the eyes of the IMAP client */
        assert(record->internal_flags & FLAG_INTERNAL_EXPUNGED);
    }

    if (oldrecord.internal_flags & FLAG_INTERNAL_ARCHIVED) {
        /* it is also a sin to unarchive a message, except in the
         * the very odd case of a reconstruct.  So let's see about
         * that */
        if (!(record->internal_flags & FLAG_INTERNAL_ARCHIVED))
            xsyslog(LOG_ERR, "IOERROR: bogus removal of archived flag",
                             "mailbox=<%s> record=<%u>",
                             mailbox_name(mailbox), record->uid);
    }

    /* handle immediate expunges here... */
    if (immediate && (record->internal_flags & FLAG_INTERNAL_EXPUNGED)) {
        record->internal_flags |= FLAG_INTERNAL_UNLINKED | FLAG_INTERNAL_NEEDS_CLEANUP;
    }

    /* make sure highestmodseq gets updated unless we're
     * being silent about it (i.e. marking an already EXPUNGED
     * message as UNLINKED, or just updating the content_lines
     * field or cache_offset) */
    if (record->silentupdate) {
        mailbox_index_dirty(mailbox);
    }
    else {
        mailbox_modseq_dirty(mailbox);
        record->modseq = mailbox->i.highestmodseq;
        record->last_updated = mailbox->last_updated;
    }
    assert(record->modseq >= oldrecord.modseq);

    if (record->internal_flags & FLAG_INTERNAL_UNLINKED) {
        /* mark required actions */
        if (expunge_mode == IMAP_ENUM_EXPUNGE_MODE_IMMEDIATE
            || mailbox->i.minor_version < 12)
            mailbox->i.options |= OPT_MAILBOX_NEEDS_REPACK;
        mailbox->i.options |= OPT_MAILBOX_NEEDS_UNLINK;
    }
    else {
        /* rewrite the cache record if required anyway */
        r = mailbox_append_cache(mailbox, record);
        if (r) return r;
    }

    r = mailbox_update_indexes(mailbox, &oldrecord, record);
    if (r) return r;

    if ((record->internal_flags & FLAG_INTERNAL_EXPUNGED) && !(changeflags & CHANGE_WASEXPUNGED)) {
        if (!mailbox->i.first_expunged || mailbox->i.first_expunged > record->last_updated)
            mailbox->i.first_expunged = record->last_updated;
        mailbox_annot_update_counts(mailbox, &oldrecord, 0);
    }

    r = _store_change(mailbox, record, changeflags);
    if (r) return r;

    if (config_auditlog) {
        char oldflags[FLAGMAPSTR_MAXLEN], sysflags[FLAGMAPSTR_MAXLEN];
        flags_to_str(&oldrecord, oldflags);
        flags_to_str(record, sysflags);
        syslog(LOG_NOTICE, "auditlog: touched sessionid=<%s> "
               "mailbox=<%s> uniqueid=<%s> uid=<%u> guid=<%s> cid=<%s> "
               "modseq=<" MODSEQ_FMT "> oldflags=<%s> sysflags=<%s>",
               session_id(), mailbox_name(mailbox), mailbox_uniqueid(mailbox),
               record->uid, message_guid_encode(&record->guid),
               conversation_id_encode(record->cid), record->modseq,
               oldflags, sysflags);
    }

    /* expunged tracking */
    if (record->internal_flags & FLAG_INTERNAL_EXPUNGED && (!mailbox->i.first_expunged || mailbox->i.first_expunged > record->last_updated))
        mailbox->i.first_expunged = record->last_updated;

    return 0;
}
EXPORTED int mailbox_rewrite_index_record(struct mailbox *mailbox,
                                          struct index_record *record)
{
    int r;
    struct index_record oldrecord;
    int expunge_mode = config_getenum(IMAPOPT_EXPUNGE_MODE);
    int immediate = (expunge_mode == IMAP_ENUM_EXPUNGE_MODE_IMMEDIATE ||
                     expunge_mode == IMAP_ENUM_EXPUNGE_MODE_SEMIDELAYED ||
                     mailbox->i.minor_version < 12);
    int changeflags = 0;

    assert(mailbox_index_islocked(mailbox, 1));
    assert(record->recno > 0 &&
           record->recno <= mailbox->i.num_records);

    r = mailbox_read_index_record(mailbox, record->recno, &oldrecord);
    if (r) {
        xsyslog(LOG_ERR, "IOERROR: re-reading record failed",
                         "mailbox=<%s> record=<%u>",
                         mailbox_name(mailbox), record->uid);
        return r;
    }
    mailbox_read_basecid(mailbox, &oldrecord);

     
    if (!record->basecid && (record->internal_flags & FLAG_INTERNAL_SPLITCONVERSATION))
        record->basecid = oldrecord.basecid;

    if (oldrecord.internal_flags & FLAG_INTERNAL_EXPUNGED)
        changeflags |= CHANGE_WASEXPUNGED;
    if (oldrecord.internal_flags & FLAG_INTERNAL_UNLINKED)
        changeflags |= CHANGE_WASUNLINKED;

     
    assert(record->uid == oldrecord.uid);
    assert(message_guid_equal(&oldrecord.guid, &record->guid));

    if (oldrecord.internal_flags & FLAG_INTERNAL_EXPUNGED) {
         
        assert(record->internal_flags & FLAG_INTERNAL_EXPUNGED);
    }

    if (oldrecord.internal_flags & FLAG_INTERNAL_ARCHIVED) {
         
        if (!(record->internal_flags & FLAG_INTERNAL_ARCHIVED))
            xsyslog(LOG_ERR, "IOERROR: bogus removal of archived flag",
                             "mailbox=<%s> record=<%u>",
                             mailbox_name(mailbox), record->uid);
    }

     
    if (immediate && (record->internal_flags & FLAG_INTERNAL_EXPUNGED)) {
        record->internal_flags |= FLAG_INTERNAL_UNLINKED | FLAG_INTERNAL_NEEDS_CLEANUP;
    }

     
    if (record->silentupdate) {
        mailbox_index_dirty(mailbox);
    }
    else {
        mailbox_modseq_dirty(mailbox);
        record->modseq = mailbox->i.highestmodseq;
        record->last_updated = mailbox->last_updated;
    }
    assert(record->modseq >= oldrecord.modseq);

    if (record->internal_flags & FLAG_INTERNAL_UNLINKED) {
         
        if (expunge_mode == IMAP_ENUM_EXPUNGE_MODE_IMMEDIATE
            || mailbox->i.minor_version < 12)
            mailbox->i.options |= OPT_MAILBOX_NEEDS_REPACK;
        mailbox->i.options |= OPT_MAILBOX_NEEDS_UNLINK;
    }
    else {
         
        r = mailbox_append_cache(mailbox, record);
        if (r) return r;
    }

    r = mailbox_update_indexes(mailbox, &oldrecord, record);
    if (r) return r;

    if ((record->internal_flags & FLAG_INTERNAL_EXPUNGED) && !(changeflags & CHANGE_WASEXPUNGED)) {
        if (!mailbox->i.first_expunged || mailbox->i.first_expunged > record->last_updated)
            mailbox->i.first_expunged = record->last_updated;
        mailbox_annot_update_counts(mailbox, &oldrecord, 0);
    }

    r = _store_change(mailbox, record, changeflags);
    if (r) return r;

    if (config_auditlog) {
        char oldflags[FLAGMAPSTR_MAXLEN], sysflags[FLAGMAPSTR_MAXLEN];
        flags_to_str(&oldrecord, oldflags);
        flags_to_str(record, sysflags);
        syslog(LOG_NOTICE, "auditlog: touched sessionid=<%s> "
               "mailbox=<%s> uniqueid=<%s> uid=<%u> guid=<%s> cid=<%s> "
               "modseq=<" MODSEQ_FMT "> oldflags=<%s> sysflags=<%s>",
               session_id(), mailbox_name(mailbox), mailbox_uniqueid(mailbox),
               record->uid, message_guid_encode(&record->guid),
               conversation_id_encode(record->cid), record->modseq,
               oldflags, sysflags);
    }

     
    if (record->internal_flags & FLAG_INTERNAL_EXPUNGED && (!mailbox->i.first_expunged || mailbox->i.first_expunged > record->last_updated))
        mailbox->i.first_expunged = record->last_updated;

    return 0;
}