maildir-mail.c revision 24cd47a2c8f7507e555459b7e841de771ba3c318
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2003-2010 Dovecot authors, see the included COPYING file */
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainendo_open(struct maildir_mailbox *mbox, const char *path,
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen mail_storage_set_critical(&mbox->storage->storage, "%s",
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen mail_storage_set_critical(&mbox->storage->storage,
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainendo_stat(struct maildir_mailbox *mbox, const char *path, struct stat *st)
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen mail_storage_set_critical(&mbox->storage->storage, "%s",
98922c5675bbbfadc84d58768bef867fe82256c2Timo Sirainen mail_storage_set_critical(&mbox->storage->storage,
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainenmaildir_rmdir_unexpected_dir(struct mail_storage *storage, const char *path)
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen "Maildir: rmdir()ed unwanted empty directory: %s",
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen "Maildir: Found unwanted directory %s, "
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic struct istream *
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainenmaildir_open_mail(struct maildir_mailbox *mbox, struct mail *mail,
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen struct mail_private *p = (struct mail_private *)mail;
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen if (maildir_file_do(mbox, mail->uid, do_open, &ctx) < 0)
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen path = maildir_save_file_get_path(mail->transaction, mail->seq);
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen /* there's a directory in maildir. many installations seem to
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen have messed up something and causing "cur", "new" and "tmp"
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen directories to be created under the "cur" directory.
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen if the directory is empty, just get rid of it and log an
c60d1eda4df179d83d531647732d5e3e45064219Timo Sirainen if (maildir_rmdir_unexpected_dir(&mbox->storage->storage,
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainenstatic int maildir_mail_stat(struct mail *mail, struct stat *st)
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)mail->box;
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen struct index_mail *imail = (struct index_mail *)mail;
ebe6df72f1309135f02b6a4d2aef1e81a073f91cTimo Sirainen if (mail->lookup_abort == MAIL_LOOKUP_ABORT_NOT_IN_CACHE)
b0a901f1dbe9e05ac1c92a0974af6bce0274f31aTimo Sirainen if (imail->data.access_part != 0 && imail->data.stream == NULL) {
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen /* we're going to open the mail anyway */
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen (void)mail_get_stream(mail, NULL, NULL, &input);
ee1a3e217279dcd0f1cccbd3442e481b7dc90f05Timo Sirainen stp = i_stream_stat(imail->data.stream, FALSE);
e9d29ae46d435aee85514decfe6ee27399ebf794Timo Sirainen ret = maildir_file_do(mbox, mail->uid, do_stat, st);
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen path = maildir_save_file_get_path(mail->transaction, mail->seq);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenstatic int maildir_mail_get_received_date(struct mail *_mail, time_t *date_r)
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen struct index_mail *mail = (struct index_mail *)_mail;
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen if (index_mail_get_received_date(_mail, date_r) == 0)
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenstatic int maildir_mail_get_save_date(struct mail *_mail, time_t *date_r)
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen struct index_mail *mail = (struct index_mail *)_mail;
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen if (index_mail_get_save_date(_mail, date_r) == 0)
3ec2c1f31631bb5ff86f5fc93a563c33e5cae90dTimo Sirainenmaildir_mail_get_fname(struct maildir_mailbox *mbox, struct mail *mail,
3ec2c1f31631bb5ff86f5fc93a563c33e5cae90dTimo Sirainen const char **fname_r)
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen ret = maildir_uidlist_lookup(mbox->uidlist, mail->uid, &flags, fname_r);
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen /* file exists in index file, but not in dovecot-uidlist anymore. */
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen /* one reason this could happen is if we delayed opening
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen dovecot-uidlist and we're trying to open a mail that got recently
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen expunged. Let's test this theory first: */
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen exists = mail_index_lookup_seq(view, mail->uid, &seq);
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen /* the message still exists in index. this means there's some
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen kind of a desync, which doesn't get fixed if cur/ mtime is
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen the same as in index. fix this by forcing a resync. */
9334fbad0aabb2fed88f40b2205d0d6f80bdffa2Timo Sirainen (void)maildir_storage_sync_force(mbox, mail->uid);
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainenstatic int maildir_get_pop3_state(struct index_mail *mail)
d22301419109ed4a38351715e6760011421dadecTimo Sirainen struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box);
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen /* if this mail itself has non-pop3 fields we know we're not
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen allowed_pop3_fields = MAIL_FETCH_FLAGS | MAIL_FETCH_STREAM_HEADER |
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen MAIL_FETCH_STREAM_BODY | MAIL_FETCH_UIDL_FILE_NAME |
8b58939517a381db55670089c0984da39fc0f099Timo Sirainen (mail->wanted_fields & ~allowed_pop3_fields) != 0)
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen /* get vsize decisions */
d22301419109ed4a38351715e6760011421dadecTimo Sirainen psize_idx = ibox->cache_fields[MAIL_CACHE_PHYSICAL_FULL_SIZE].idx;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen vsize_idx = ibox->cache_fields[MAIL_CACHE_VIRTUAL_FULL_SIZE].idx;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen vsize_dec = mail_cache_field_get_decision(box->cache,
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen /* also check if there are any non-[pv]size cached fields */
d22301419109ed4a38351715e6760011421dadecTimo Sirainen fields = mail_cache_register_get_list(box->cache,
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen for (i = 0; i < count; i++) {
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen dec = fields[i].decision & ~MAIL_CACHE_DECISION_FORCED;
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen /* either nothing is cached, or only vsize is cached. */
236bedf76e31651ea9fca63fbdc25be673819526Timo Sirainen } else if (vsize_dec != MAIL_CACHE_DECISION_YES &&
d22301419109ed4a38351715e6760011421dadecTimo Sirainen (box->flags & MAILBOX_FLAG_POP3_SESSION) == 0) {
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen /* if virtual size isn't cached permanently,
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen POP3 isn't being used */
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen /* possibly a mixed pop3/imap */
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainenstatic int maildir_quick_size_lookup(struct index_mail *mail, bool vsize,
d22301419109ed4a38351715e6760011421dadecTimo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)_mail->box;
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen if (maildir_mail_get_fname(mbox, _mail, &fname) <= 0)
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen path = maildir_save_file_get_path(_mail->transaction,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* size can be included in filename */
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen /* size can be included in uidlist entry */
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen value = maildir_uidlist_lookup_ext(mbox->uidlist, _mail->uid,
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen if (value != NULL && str_to_uoff(value, size_r) == 0)
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainenmaildir_handle_size_caching(struct index_mail *mail, bool quick_check,
d22301419109ed4a38351715e6760011421dadecTimo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)box;
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen field = vsize ? MAIL_FETCH_VIRTUAL_SIZE : MAIL_FETCH_PHYSICAL_SIZE;
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen if ((mail->data.dont_cache_fetch_fields & field) != 0)
55b6e3105184ad6a2f987346380966f556300055Timo Sirainen if (quick_check && maildir_quick_size_lookup(mail, vsize, &size) > 0) {
7aeaf23f760d86aad525d831efcac9f860a55a39Timo Sirainen /* already in filename / uidlist. don't add it anywhere,
cf49fc07f541c0f74578ac6c3b334ddade143aa1Timo Sirainen including to the uidlist if it's already in filename.
cf49fc07f541c0f74578ac6c3b334ddade143aa1Timo Sirainen do some extra checks here to catch potential cache bugs. */
cf49fc07f541c0f74578ac6c3b334ddade143aa1Timo Sirainen if (vsize && mail->data.virtual_size != size) {
63aaafe7e6b201d6633f8c25610ecd30c9cda99cTimo Sirainen "Corrupted virtual size for uid=%u: "
cf49fc07f541c0f74578ac6c3b334ddade143aa1Timo Sirainen } else if (!vsize && mail->data.physical_size != size) {
63aaafe7e6b201d6633f8c25610ecd30c9cda99cTimo Sirainen "Corrupted physical size for uid=%u: "
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen /* 1 = pop3-only, 0 = mixed, -1 = no pop3 */
18065635d4e79dd96eb3b3215718abd12f6a6808Timo Sirainen if (pop3_state >= 0 && mail->mail.mail.uid != 0) {
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen /* if size is wanted permanently, store it to uidlist
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen so that in case cache file gets lost we can get it quickly */
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen maildir_uidlist_set_ext(mbox->uidlist, mail->mail.mail.uid,
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainenstatic int maildir_mail_get_virtual_size(struct mail *_mail, uoff_t *size_r)
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen struct index_mail *mail = (struct index_mail *)_mail;
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen if (index_mail_get_cached_virtual_size(mail, size_r)) {
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen i_assert(mail->data.virtual_size != (uoff_t)-1);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen maildir_handle_size_caching(mail, TRUE, TRUE);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen if (maildir_quick_size_lookup(mail, TRUE, &data->virtual_size) < 0)
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen data->dont_cache_fetch_fields |= MAIL_FETCH_VIRTUAL_SIZE;
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen /* fallback to reading the file */
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen old_offset = data->stream == NULL ? 0 : data->stream->v_offset;
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen if (mail_get_stream(_mail, &hdr_size, &body_size, &input) < 0)
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen maildir_handle_size_caching(mail, FALSE, TRUE);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainenstatic int maildir_mail_get_physical_size(struct mail *_mail, uoff_t *size_r)
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen struct index_mail *mail = (struct index_mail *)_mail;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)_mail->box;
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen if (index_mail_get_physical_size(_mail, size_r) == 0) {
9fdd27b307347a06871ddab74b165122c878553cTimo Sirainen i_assert(mail->data.physical_size != (uoff_t)-1);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen maildir_handle_size_caching(mail, TRUE, FALSE);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen if (maildir_quick_size_lookup(mail, FALSE, &data->physical_size) < 0)
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen data->dont_cache_fetch_fields |= MAIL_FETCH_PHYSICAL_SIZE;
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen ret = maildir_file_do(mbox, _mail->uid, do_stat, &st);
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen /* saved mail which hasn't been committed yet */
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen path = maildir_save_file_get_path(_mail->transaction,
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen mail_storage_set_critical(_mail->box->storage,
97eb53ade9057e6966dbb77289ad0204c7e1657bTimo Sirainen maildir_handle_size_caching(mail, FALSE, FALSE);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenmaildir_mail_get_special(struct mail *_mail, enum mail_fetch_field field,
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen const char **value_r)
366d6311c9d5bac6613e3cd64619eb878adce9ecTimo Sirainen struct index_mail *mail = (struct index_mail *)_mail;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)_mail->box;
24cd47a2c8f7507e555459b7e841de771ba3c318Timo Sirainen const char *path, *fname = NULL, *end, *guid, *uidl;
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen /* use GUID from uidlist if it exists */
24cd47a2c8f7507e555459b7e841de771ba3c318Timo Sirainen /* first make sure that we have a refreshed uidlist */
24cd47a2c8f7507e555459b7e841de771ba3c318Timo Sirainen if (maildir_mail_get_fname(mbox, _mail, &fname) <= 0)
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen guid = maildir_uidlist_lookup_ext(mbox->uidlist, _mail->uid,
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen /* default to base filename: */
24cd47a2c8f7507e555459b7e841de771ba3c318Timo Sirainen /* we came here from MAIL_FETCH_GUID,
24cd47a2c8f7507e555459b7e841de771ba3c318Timo Sirainen avoid a second lookup */
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen if (maildir_mail_get_fname(mbox, _mail, &fname) <= 0)
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen path = maildir_save_file_get_path(_mail->transaction,
e76c494ad6535d3de314cc0d3ac7a44b06e53c4bTimo Sirainen uidl = maildir_uidlist_lookup_ext(mbox->uidlist, _mail->uid,
89fb98e9eb7e95255a579c8e9d865383c2334a74Timo Sirainen /* use the default */
89fb98e9eb7e95255a579c8e9d865383c2334a74Timo Sirainen /* special optimization case: use the base file name */
b19a1420da0618a10edf67c2cfd13c8c8633057aTimo Sirainen return index_mail_get_special(_mail, field, value_r);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainenstatic int maildir_mail_get_stream(struct mail *_mail,
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen struct index_mail *mail = (struct index_mail *)_mail;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)_mail->box;
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen data->stream = maildir_open_mail(mbox, _mail, &deleted);
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen return index_mail_init_stream(mail, hdr_size, body_size, stream_r);
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainenstatic void maildir_update_pop3_uidl(struct mail *_mail, const char *uidl)
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)_mail->box;
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen if (maildir_mail_get_special(_mail, MAIL_FETCH_UIDL_FILE_NAME,
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen /* special case optimization: empty UIDL means the same
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen as base filename */
225e82df5dd1e765f4e52b80c954558f00e5a7dfTimo Sirainen maildir_uidlist_set_ext(mbox->uidlist, _mail->uid,
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainenstatic void maildir_mail_set_cache_corrupted(struct mail *_mail,
d22301419109ed4a38351715e6760011421dadecTimo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)_mail->box;
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen /* make sure it gets removed from uidlist.
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen if it's in file name, we can't really do more than log it. */
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen ret = maildir_uidlist_lookup(mbox->uidlist, _mail->uid,
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen if (maildir_filename_get_size(fname, MAILDIR_EXTRA_VIRTUAL_SIZE,
e0645d5232900a5c574780ef0c0362a5b7581abaTimo Sirainen const char *subdir =
e0645d5232900a5c574780ef0c0362a5b7581abaTimo Sirainen (flags & MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) != 0 ?
0d16525a729011f4fced989a3da74d755ea49e6dTimo Sirainen mail_storage_set_critical(_mail->box->storage,
e0645d5232900a5c574780ef0c0362a5b7581abaTimo Sirainen "Maildir filename has wrong W value: %s/%s/%s",
8c9e48cd6de80da0fa32b9c0dee003472c9a7c0dTimo Sirainen } else if (maildir_uidlist_lookup_ext(mbox->uidlist, _mail->uid,