/* * 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, ©record); if (r) goto done; } done: mailbox_iter_done(&iter); mailbox_close(&mailbox); return r; }
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; }
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); }
/* 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; }
/* * 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; }