Ejemplo n.º 1
0
/*
 * mboxlist_findall() callback function to examine a mailbox
 */
static int do_timestamp(const char *name)
{
    int r = 0;
    char ext_name_buf[MAX_MAILBOX_PATH+1];
    struct mailbox *mailbox = NULL;
    const struct index_record *record;
    char olddate[RFC822_DATETIME_MAX+1];
    char newdate[RFC822_DATETIME_MAX+1];

    signals_poll();

    /* Convert internal name to external */
    (*recon_namespace.mboxname_toexternal)(&recon_namespace, name,
                                           "cyrus", ext_name_buf);
    printf("Working on %s...\n", ext_name_buf);

    /* Open/lock header */
    r = mailbox_open_iwl(name, &mailbox);
    if (r) return r;

    struct mailbox_iter *iter = mailbox_iter_init(mailbox, 0, ITER_SKIP_EXPUNGED);
    while ((record = mailbox_iter_step(iter))) {
        /* 1 day is close enough */
        if (labs(record->internaldate - record->gmtime) < 86400)
            continue;

        struct index_record copyrecord = *record;

        time_to_rfc822(copyrecord.internaldate, olddate, sizeof(olddate));
        time_to_rfc822(copyrecord.gmtime, newdate, sizeof(newdate));
        printf("  %u: %s => %s\n", copyrecord.uid, olddate, newdate);

        /* switch internaldate */
        copyrecord.internaldate = copyrecord.gmtime;

        r = mailbox_rewrite_index_record(mailbox, &copyrecord);
        if (r) goto done;
    }

 done:
    mailbox_iter_done(&iter);
    mailbox_close(&mailbox);

    return r;
}
Ejemplo n.º 2
0
static int restore_expunged(struct mailbox *mailbox, int mode, unsigned long *uids,
                     unsigned nuids, time_t time_since, unsigned *numrestored,
                     const char *extname)
{
    struct index_record newrecord;
    annotate_state_t *astate = NULL;
    unsigned uidnum = 0;
    char oldfname[MAX_MAILBOX_PATH];
    const char *fname;
    char *userid = mboxname_to_userid(mailbox->name);
    int r = 0;

    *numrestored = 0;

    struct mailbox_iter *iter = mailbox_iter_init(mailbox, 0, ITER_SKIP_UNLINKED);
    const message_t *msg;
    while ((msg = mailbox_iter_step(iter))) {
        const struct index_record *record = msg_record(msg);
        /* still active */
        if (!(record->internal_flags & FLAG_INTERNAL_EXPUNGED))
            continue;

        if (mode == MODE_UID) {
            while (uidnum < nuids && record->uid > uids[uidnum])
                uidnum++;
            if (uidnum >= nuids)
                continue;
            if (record->uid != uids[uidnum])
                continue;
            /* otherwise we want this one */
        }
        else if (mode == MODE_TIME) {
            if (record->last_updated < time_since)
                continue;
            /* otherwise we want this one */
        }

        /* work on a copy */
        newrecord = *record;

        /* duplicate the old filename */
        fname = mailbox_record_fname(mailbox, record);
        xstrncpy(oldfname, fname, MAX_MAILBOX_PATH);

        /* bump the UID, strip the flags */
        newrecord.uid = mailbox->i.last_uid + 1;
        newrecord.internal_flags &= ~FLAG_INTERNAL_EXPUNGED;
        if (unsetdeleted)
            newrecord.system_flags &= ~FLAG_DELETED;

        /* copy the message file */
        fname = mailbox_record_fname(mailbox, &newrecord);
        r = mailbox_copyfile(oldfname, fname, 0);
        if (r) break;

        /* add the flag if requested */
        if (addflag) {
            int userflag = 0;
            r = mailbox_user_flag(mailbox, addflag, &userflag, 1);
            if (r) break;
            newrecord.user_flags[userflag/32] |= 1<<(userflag&31);
        }

        /* and append the new record */
        r = mailbox_append_index_record(mailbox, &newrecord);
        if (r) break;

        /* ensure we have an astate connected to the destination
         * mailbox, so that the annotation txn will be committed
         * when we close the mailbox */
        r = mailbox_get_annotate_state(mailbox, newrecord.uid, &astate);
        if (r) break;

        /* and copy over any annotations */
        r = annotate_msg_copy(mailbox, record->uid,
                              mailbox, newrecord.uid,
                              userid);
        if (r) break;

        if (verbose)
            printf("Unexpunged %s: %u => %u\n",
                   extname, record->uid, newrecord.uid);

        /* mark the old one unlinked so we don't see it again */
        struct index_record oldrecord = *record;
        oldrecord.internal_flags |= FLAG_INTERNAL_UNLINKED |
            FLAG_INTERNAL_NEEDS_CLEANUP;
        r = mailbox_rewrite_index_record(mailbox, &oldrecord);
        if (r) break;

        (*numrestored)++;
    }

    /* better get that seen to */
    if (*numrestored)
        mailbox->i.options |= OPT_MAILBOX_NEEDS_UNLINK;

    mailbox_iter_done(&iter);
    free(userid);
    return r;
}
Ejemplo n.º 3
0
static void list_expunged(const char *mboxname)
{
    struct mailbox *mailbox = NULL;
    struct index_record *records = NULL;
    int alloc = 0;
    int num = 0;
    int i;
    int r;

    r = mailbox_open_irl(mboxname, &mailbox);
    if (r) {
        printf("Failed to open mailbox %s: %s",
               mboxname, error_message(r));
        return;
    }

    /* first pass - read the records.  Don't print until we release the
     * lock */
    struct mailbox_iter *iter = mailbox_iter_init(mailbox, 0, ITER_SKIP_UNLINKED);
    const message_t *msg;
    while ((msg = mailbox_iter_step(iter))) {
        const struct index_record *record = msg_record(msg);
        /* still active */
        if (!(record->internal_flags & FLAG_INTERNAL_EXPUNGED))
            continue;

        /* pre-allocate more space */
        if (alloc <= num) {
            alloc += 64;
            records = xrealloc(records, sizeof(struct index_record) * alloc);
        }

        records[num] = *record;
        num++;
    }
    mailbox_iter_done(&iter);

    mailbox_unlock_index(mailbox, NULL);

    for (i = 0; i < num; i++) {
        const struct index_record *record = &records[i];
        printf("UID: %u\n", record->uid);
        printf("\tSize: %u\n", record->size);
        printf("\tSent: %s", ctime(&record->sentdate));
        printf("\tRecv: %s", ctime(&record->internaldate));
        printf("\tExpg: %s", ctime(&record->last_updated));

        if (mailbox_cacherecord(mailbox, record)) {
            printf("\tERROR: cache record missing or corrupt, "
                   "not printing cache details\n\n");
            continue;
        }

        printf("\tFrom: %.*s\n", cacheitem_size(record, CACHE_FROM),
                cacheitem_base(record, CACHE_FROM));
        printf("\tTo  : %.*s\n", cacheitem_size(record, CACHE_TO),
                cacheitem_base(record, CACHE_TO));
        printf("\tCc  : %.*s\n", cacheitem_size(record, CACHE_CC),
                cacheitem_base(record, CACHE_CC));
        printf("\tBcc : %.*s\n", cacheitem_size(record, CACHE_BCC),
                cacheitem_base(record, CACHE_BCC));
        printf("\tSubj: %.*s\n\n", cacheitem_size(record, CACHE_SUBJECT),
                cacheitem_base(record, CACHE_SUBJECT));
    }

    free(records);
    mailbox_close(&mailbox);
}
Ejemplo n.º 4
0
/* create a downgraded index file in cyrus.index.  We don't copy back
 * expunged messages, sorry */
static int dump_index(struct mailbox *mailbox, int oldversion,
                      struct seqset *expunged_seq, int first, int sync,
                      struct protstream *pin, struct protstream *pout)
{
    char oldname[MAX_MAILBOX_PATH];
    const char *fname;
    int oldindex_fd = -1;
    indexbuffer_t headerbuf;
    indexbuffer_t recordbuf;
    char *hbuf = (char *)headerbuf.buf;
    char *rbuf = (char *)recordbuf.buf;
    int header_size;
    int record_size;
    int n, r;

    if (oldversion == 6) {
        header_size = 76;
        record_size = 60;
    }
    else if (oldversion == 7) {
        header_size = 76;
        record_size = 72;
    }
    else if (oldversion == 8) {
        header_size = 92;
        record_size = 80;
    }
    else if (oldversion == 9) {
        header_size = 96;
        record_size = 80;
    }
    else if (oldversion == 10) {
        header_size = 96;
        record_size = 88;
    }
    else {
        return IMAP_MAILBOX_BADFORMAT;
    }

    fname = mailbox_meta_fname(mailbox, META_INDEX);
    snprintf(oldname, MAX_MAILBOX_PATH, "%s.OLD", fname);

    oldindex_fd = open(oldname, O_RDWR|O_TRUNC|O_CREAT, 0666);
    if (oldindex_fd == -1) goto fail;

    downgrade_header(&mailbox->i, hbuf, oldversion,
                     header_size, record_size);

    /* Write header - everything we'll say */
    n = retry_write(oldindex_fd, hbuf, header_size);
    if (n == -1) goto fail;

    struct mailbox_iter *iter = mailbox_iter_init(mailbox, 0, ITER_SKIP_UNLINKED);

    const message_t *msg;

    while ((msg = mailbox_iter_step(iter))) {
        const struct index_record *record = msg_record(msg);
        /* we have to make sure expunged records don't get the
         * file copied, or a reconstruct could bring them back
         * to life!  It we're not creating an expunged file... */
        if (record->system_flags & FLAG_EXPUNGED) {
            if (oldversion < 9) seqset_add(expunged_seq, record->uid, 1);
            continue;
        }
        /* not making sure exists matches, we do trust a bit */
        downgrade_record(record, rbuf, oldversion);
        n = retry_write(oldindex_fd, rbuf, record_size);
        if (n == -1) goto fail;
    }

    mailbox_iter_done(&iter);

    close(oldindex_fd);
    r = dump_file(first, sync, pin, pout, oldname, "cyrus.index", NULL, 0);
    unlink(oldname);
    if (r) return r;

    /* create cyrus.expunge */
    if (oldversion > 8 && mailbox->i.num_records > mailbox->i.exists) {
        int nexpunge = mailbox->i.num_records - mailbox->i.exists;
        fname = mailbox_meta_fname(mailbox, META_EXPUNGE);
        snprintf(oldname, MAX_MAILBOX_PATH, "%s.OLD", fname);

        oldindex_fd = open(oldname, O_RDWR|O_TRUNC|O_CREAT, 0666);
        if (oldindex_fd == -1) goto fail;

        header_set_num_records(hbuf, nexpunge);

        /* Write header - everything we'll say */
        n = retry_write(oldindex_fd, hbuf, header_size);
        if (n == -1) goto fail;

        iter = mailbox_iter_init(mailbox, 0, ITER_SKIP_UNLINKED);
        while ((msg = mailbox_iter_step(iter))) {
            const struct index_record *record = msg_record(msg);
            /* ignore non-expunged records */
            if (!(record->system_flags & FLAG_EXPUNGED))
                continue;
            downgrade_record(record, rbuf, oldversion);
            n = retry_write(oldindex_fd, rbuf, record_size);
            if (n == -1) goto fail;
        }

        close(oldindex_fd);
        r = dump_file(first, sync, pin, pout, oldname, "cyrus.expunge", NULL, 0);
        unlink(oldname);
        if (r) return r;
    }

    return 0;

fail:
    if (oldindex_fd != -1)
        close(oldindex_fd);
    unlink(oldname);

    return IMAP_IOERROR;
}
Ejemplo n.º 5
0
/*
 * Performs a STATUS command - note: state MAY be NULL here.
 */
EXPORTED int status_lookup(const char *mboxname, const char *userid,
                  unsigned statusitems, struct statusdata *sdata)
{
    struct mailbox *mailbox = NULL;
    unsigned numrecent = 0;
    unsigned numunseen = 0;
    unsigned c_statusitems;
    int r;

    /* Check status cache if possible */
    if (config_getswitch(IMAPOPT_STATUSCACHE)) {
        /* Do actual lookup of cache item. */
        r = statuscache_lookup(mboxname, userid, statusitems, sdata);

        /* Seen/recent status uses "push" invalidation events from
         * seen_db.c.   This avoids needing to open cyrus.header to get
         * the mailbox uniqueid to open the seen db and get the
         * unseen_mtime and recentuid.
         */

        if (!r) {
            syslog(LOG_DEBUG, "statuscache, '%s', '%s', '0x%02x', 'yes'",
                   mboxname, userid, statusitems);
            return 0;
        }

        syslog(LOG_DEBUG, "statuscache, '%s', '%s', '0x%02x', 'no'",
               mboxname, userid, statusitems);
    }

    /* Missing or invalid cache entry */
    r = mailbox_open_irl(mboxname, &mailbox);
    if (r) return r;

    /* We always have message count, uidnext,
       uidvalidity, and highestmodseq for cache */
    c_statusitems = STATUS_MESSAGES | STATUS_UIDNEXT |
                    STATUS_UIDVALIDITY | STATUS_HIGHESTMODSEQ;

    if (!mailbox->i.exists) {
        /* no messages, so these two must also be zero */
        c_statusitems |= STATUS_RECENT | STATUS_UNSEEN;
    }
    else if (statusitems & (STATUS_RECENT | STATUS_UNSEEN)) {
        /* Read \Seen state */
        struct seqset *seq = NULL;
        const struct index_record *record;
        int internalseen = mailbox_internal_seen(mailbox, userid);
        unsigned recentuid;

        if (internalseen) {
            recentuid = mailbox->i.recentuid;
        } else {
            struct seen *seendb = NULL;
            struct seendata sd = SEENDATA_INITIALIZER;

            r = seen_open(userid, SEEN_CREATE, &seendb);
            if (!r) r = seen_read(seendb, mailbox->uniqueid, &sd);
            seen_close(&seendb);
            if (r) goto done;

            recentuid = sd.lastuid;
            seq = seqset_parse(sd.seenuids, NULL, recentuid);
            seen_freedata(&sd);
        }

        struct mailbox_iter *iter = mailbox_iter_init(mailbox, 0, ITER_SKIP_EXPUNGED);
        while ((record = mailbox_iter_step(iter))) {
            if (record->uid > recentuid)
                numrecent++;
            if (internalseen) {
                if (!(record->system_flags & FLAG_SEEN))
                    numunseen++;
            }
            else {
                if (!seqset_ismember(seq, record->uid))
                    numunseen++;
            }
        }
        mailbox_iter_done(&iter);

        /* we've calculated the correct values for both */
        c_statusitems |= STATUS_RECENT | STATUS_UNSEEN;
    }

    statuscache_fill(sdata, userid, mailbox, c_statusitems,
                     numrecent, numunseen);

    /* cache the new value while unlocking */
    mailbox_unlock_index(mailbox, sdata);

  done:
    mailbox_close(&mailbox);
    return r;
}