maildir-mail.c revision c60d1eda4df179d83d531647732d5e3e45064219
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen/* Copyright (c) 2003-2010 Dovecot authors, see the included COPYING file */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainendo_open(struct maildir_mailbox *mbox, const char *path,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen mail_storage_set_critical(&mbox->storage->storage, "%s",
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen mail_storage_set_critical(&mbox->storage->storage,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainendo_stat(struct maildir_mailbox *mbox, const char *path, struct stat *st)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen mail_storage_set_critical(&mbox->storage->storage, "%s",
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen mail_storage_set_critical(&mbox->storage->storage,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenmaildir_rmdir_unexpected_dir(struct mail_storage *storage, const char *path)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen "Maildir: rmdir()ed unwanted empty directory: %s",
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen "Maildir: Found unwanted directory %s, "
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic struct istream *
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenmaildir_open_mail(struct maildir_mailbox *mbox, struct mail *mail,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen struct mail_private *p = (struct mail_private *)mail;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (maildir_file_do(mbox, mail->uid, do_open, &ctx) < 0)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen path = maildir_save_file_get_path(mail->transaction, mail->seq);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen /* there's a directory in maildir. many installations seem to
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen have messed up something and causing "cur", "new" and "tmp"
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen directories to be created under the "cur" directory.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if the directory is empty, just get rid of it and log an
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (maildir_rmdir_unexpected_dir(&mbox->storage->storage,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic int maildir_mail_stat(struct mail *mail, struct stat *st)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)mail->box;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen struct index_mail *imail = (struct index_mail *)mail;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (mail->lookup_abort == MAIL_LOOKUP_ABORT_NOT_IN_CACHE)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (imail->data.access_part != 0 && imail->data.stream == NULL) {
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen /* we're going to open the mail anyway */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen (void)mail_get_stream(mail, NULL, NULL, &input);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen stp = i_stream_stat(imail->data.stream, FALSE);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen ret = maildir_file_do(mbox, mail->uid, do_stat, st);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen path = maildir_save_file_get_path(mail->transaction, mail->seq);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic int maildir_mail_get_received_date(struct mail *_mail, time_t *date_r)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen struct index_mail *mail = (struct index_mail *)_mail;
16f816d3f3c32ae3351834253f52ddd0212bcbf3Timo Sirainen if (index_mail_get_received_date(_mail, date_r) == 0)
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainenstatic int maildir_mail_get_save_date(struct mail *_mail, time_t *date_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct index_mail *mail = (struct index_mail *)_mail;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (index_mail_get_save_date(_mail, date_r) == 0)
d3eff05aaa4c2bc0a7580ee87a54f6693f4a8241Timo Sirainenmaildir_mail_get_fname(struct maildir_mailbox *mbox, struct mail *mail,
d3eff05aaa4c2bc0a7580ee87a54f6693f4a8241Timo Sirainen const char **fname_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = maildir_uidlist_lookup(mbox->uidlist, mail->uid, &flags, fname_r);
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen /* file exists in index file, but not in dovecot-uidlist anymore. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* one reason this could happen is if we delayed opening
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen dovecot-uidlist and we're trying to open a mail that got recently
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen expunged. Let's test this theory first: */
b35f7104715edee0cfac6d46ab0b342033867eb7Timo Sirainen exists = mail_index_lookup_seq(view, mail->uid, &seq);
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen /* the message still exists in index. this means there's some
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen kind of a desync, which doesn't get fixed if cur/ mtime is
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen the same as in index. fix this by forcing a resync. */
b92813e2f96d4b28f989528ed5dd6115da7d9bdbTimo Sirainen (void)maildir_storage_sync_force(mbox, mail->uid);
e1ca7af110ea6eeb6303bdd8f07c172b11dff2faTimo Sirainenstatic int maildir_get_pop3_state(struct index_mail *mail)
e1ca7af110ea6eeb6303bdd8f07c172b11dff2faTimo Sirainen struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* if this mail itself has non-pop3 fields we know we're not
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen allowed_pop3_fields = MAIL_FETCH_FLAGS | MAIL_FETCH_STREAM_HEADER |
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen MAIL_FETCH_STREAM_BODY | MAIL_FETCH_UIDL_FILE_NAME |
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen (mail->wanted_fields & ~allowed_pop3_fields) != 0)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* get vsize decisions */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen psize_idx = ibox->cache_fields[MAIL_CACHE_PHYSICAL_FULL_SIZE].idx;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen vsize_idx = ibox->cache_fields[MAIL_CACHE_VIRTUAL_FULL_SIZE].idx;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen vsize_dec = mail_cache_field_get_decision(box->cache,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* also check if there are any non-[pv]size cached fields */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen fields = mail_cache_register_get_list(box->cache,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen for (i = 0; i < count; i++) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen dec = fields[i].decision & ~MAIL_CACHE_DECISION_FORCED;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* either nothing is cached, or only vsize is cached. */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen } else if (vsize_dec != MAIL_CACHE_DECISION_YES &&
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen (box->flags & MAILBOX_FLAG_POP3_SESSION) == 0) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* if virtual size isn't cached permanently,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen POP3 isn't being used */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* possibly a mixed pop3/imap */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenstatic int maildir_quick_size_lookup(struct index_mail *mail, bool vsize,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)_mail->box;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (maildir_mail_get_fname(mbox, _mail, &fname) <= 0)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen path = maildir_save_file_get_path(_mail->transaction,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* size can be included in filename */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* size can be included in uidlist entry */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen value = maildir_uidlist_lookup_ext(mbox->uidlist, _mail->uid,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (value != NULL && str_to_uoff(value, size_r) == 0)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenmaildir_handle_size_caching(struct index_mail *mail, bool quick_check,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)box;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen field = vsize ? MAIL_FETCH_VIRTUAL_SIZE : MAIL_FETCH_PHYSICAL_SIZE;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if ((mail->data.dont_cache_fetch_fields & field) != 0)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (quick_check && maildir_quick_size_lookup(mail, vsize, &size) > 0) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* already in filename / uidlist. don't add it anywhere,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen including to the uidlist if it's already in filename.
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen do some extra checks here to catch potential cache bugs. */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (vsize && mail->data.virtual_size != size) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen "Corrupted virtual size for uid=%u: "
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen } else if (!vsize && mail->data.physical_size != size) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen "Corrupted physical size for uid=%u: "
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* 1 = pop3-only, 0 = mixed, -1 = no pop3 */
e1ca7af110ea6eeb6303bdd8f07c172b11dff2faTimo Sirainen if (pop3_state >= 0 && mail->mail.mail.uid != 0) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* if size is wanted permanently, store it to uidlist
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen so that in case cache file gets lost we can get it quickly */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen maildir_uidlist_set_ext(mbox->uidlist, mail->mail.mail.uid,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenstatic int maildir_mail_get_virtual_size(struct mail *_mail, uoff_t *size_r)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen struct index_mail *mail = (struct index_mail *)_mail;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (index_mail_get_cached_virtual_size(mail, size_r)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(mail->data.virtual_size != (uoff_t)-1);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen maildir_handle_size_caching(mail, TRUE, TRUE);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (maildir_quick_size_lookup(mail, TRUE, &data->virtual_size) < 0)
8153fdec343e40e2a78f5c12353e89b994b28f74Timo Sirainen data->dont_cache_fetch_fields |= MAIL_FETCH_VIRTUAL_SIZE;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen /* fallback to reading the file */
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen old_offset = data->stream == NULL ? 0 : data->stream->v_offset;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (mail_get_stream(_mail, &hdr_size, &body_size, &input) < 0)
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen maildir_handle_size_caching(mail, FALSE, TRUE);
b35f7104715edee0cfac6d46ab0b342033867eb7Timo Sirainenstatic int maildir_mail_get_physical_size(struct mail *_mail, uoff_t *size_r)
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen struct index_mail *mail = (struct index_mail *)_mail;
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)_mail->box;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (index_mail_get_physical_size(_mail, size_r) == 0) {
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen i_assert(mail->data.physical_size != (uoff_t)-1);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen maildir_handle_size_caching(mail, TRUE, FALSE);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if (maildir_quick_size_lookup(mail, FALSE, &data->physical_size) < 0)
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen data->dont_cache_fetch_fields |= MAIL_FETCH_PHYSICAL_SIZE;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen ret = maildir_file_do(mbox, _mail->uid, do_stat, &st);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* saved mail which hasn't been committed yet */
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen path = maildir_save_file_get_path(_mail->transaction,
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen mail_storage_set_critical(_mail->box->storage,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen maildir_handle_size_caching(mail, FALSE, FALSE);
b92813e2f96d4b28f989528ed5dd6115da7d9bdbTimo Sirainenmaildir_mail_get_special(struct mail *_mail, enum mail_fetch_field field,
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen const char **value_r)
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen struct index_mail *mail = (struct index_mail *)_mail;
8153fdec343e40e2a78f5c12353e89b994b28f74Timo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)_mail->box;
6bf1543bb7af03324c04e8f9ac8e430f395989aeTimo Sirainen if (maildir_mail_get_fname(mbox, _mail, &fname) <= 0)
6bf1543bb7af03324c04e8f9ac8e430f395989aeTimo Sirainen path = maildir_save_file_get_path(_mail->transaction,
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen uidl = maildir_uidlist_lookup_ext(mbox->uidlist, _mail->uid,
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen /* use the default */
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen /* special optimization case: use the base file name */
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen return index_mail_get_special(_mail, field, value_r);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainenstatic int maildir_mail_get_stream(struct mail *_mail,
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen struct index_mail *mail = (struct index_mail *)_mail;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)_mail->box;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen data->stream = maildir_open_mail(mbox, _mail, &deleted);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen return index_mail_init_stream(mail, hdr_size, body_size, stream_r);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainenstatic void maildir_update_pop3_uidl(struct mail *_mail, const char *uidl)
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)_mail->box;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if (maildir_mail_get_special(_mail, MAIL_FETCH_UIDL_FILE_NAME,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* special case optimization: empty UIDL means the same
659fe5d24825b160cae512538088020d97a60239Timo Sirainen as base filename */
6f66e585998aa88a4b0ccad531d329a103325d57Timo Sirainen maildir_uidlist_set_ext(mbox->uidlist, _mail->uid,
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainenstatic void maildir_mail_set_cache_corrupted(struct mail *_mail,
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)_mail->box;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* make sure it gets removed from uidlist.
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if it's in file name, we can't really do more than log it. */
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen ret = maildir_uidlist_lookup(mbox->uidlist, _mail->uid,
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen if (maildir_filename_get_size(fname, MAILDIR_EXTRA_VIRTUAL_SIZE,
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen const char *subdir =
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen (flags & MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) != 0 ?
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen mail_storage_set_critical(_mail->box->storage,
b35f7104715edee0cfac6d46ab0b342033867eb7Timo Sirainen "Maildir filename has wrong W value: %s/%s/%s",
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen } else if (maildir_uidlist_lookup_ext(mbox->uidlist, _mail->uid,