pop3-migration-plugin.c revision 0dffa25d211be541ee3c953b23566a1a990789df
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2007-2016 Dovecot authors, see the included COPYING file */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MODULE_CONTEXT(obj, pop3_migration_storage_module)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MODULE_CONTEXT(obj, pop3_migration_mail_module)
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen /* sha1(header) - set only when needed */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* LIST size */
35ef661bd85c64834e3e34eeeb3c393b81108760Timo Sirainen/* NOTE: these headers must be sorted */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic const char *hdr_hash_skip_headers[] = {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "Content-Length",
35ef661bd85c64834e3e34eeeb3c393b81108760Timo Sirainen "Return-Path", /* Yahoo IMAP has Return-Path, Yahoo POP3 doesn't */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "X-IMAPbase",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "X-Keywords",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "X-Message-Flag",
c39c3d8089fbdd8eb34646c25167aa4551064cf4Timo Sirainen "X-Yahoo-Newman-Property"
bd63b5b860658b01b1f46f26d406e1e4a9dc019aTimo Sirainenconst char *pop3_migration_plugin_version = DOVECOT_ABI_VERSION;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(pop3_migration_storage_module,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(pop3_migration_mail_module,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int imap_msg_map_uid_cmp(const struct imap_msg_map *map1,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int pop3_uidl_map_pop3_seq_cmp(const struct pop3_uidl_map *map1,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int pop3_uidl_map_hdr_cmp(const struct pop3_uidl_map *map1,
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen return memcmp(map1->common.hdr_sha1, map2->common.hdr_sha1,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int imap_msg_map_hdr_cmp(const struct imap_msg_map *map1,
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen return memcmp(map1->common.hdr_sha1, map2->common.hdr_sha1,
5fa253bd316540ec280ca76b39d62a9e32da228bTimo Sirainenstatic bool header_name_is_valid(const char *name)
5fa253bd316540ec280ca76b39d62a9e32da228bTimo Sirainen unsigned int i;
5fa253bd316540ec280ca76b39d62a9e32da228bTimo Sirainen if ((uint8_t)name[i] <= 0x20 || name[i] >= 0x7f)
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainenpop3_header_filter_callback(struct header_filter_istream *input ATTR_UNUSED,
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainen if (strspn(hdr->name, "\r") == hdr->name_len) {
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainen /* CR+CR+LF - some servers stop the header processing
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainen here while others don't. To make sure they can be
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainen matched correctly we want to stop here entirely. */
5fa253bd316540ec280ca76b39d62a9e32da228bTimo Sirainen /* Yahoo IMAP drops headers with invalid names, while
5fa253bd316540ec280ca76b39d62a9e32da228bTimo Sirainen Yahoo POP3 preserves them. Drop them all. */
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainenint pop3_migration_get_hdr_sha1(uint32_t mail_seq, struct istream *input,
9625595c47c665f5aee57ebfcb1fcbe9ad1bf3a0Martti Rannanjärvi unsigned char sha1_r[STATIC_ARRAY SHA1_RESULTLEN],
2f19f8ff906a0017b906763e0f7675d49ab0e58fTimo Sirainen const unsigned char *data;
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen input2 = i_stream_create_limit(input, hdr_size);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* hide headers that might change or be different in IMAP vs. POP3 */
3858a7a5da361c35f1e6e50c8e3214dc0cf379d6Phil Carmody while (i_stream_read_more(input, &data, &size) > 0) {
06fda713b84e857dbc3e80f401a54085c9b0ed16Timo Sirainen message_header_hash_more(&hash_method_sha1, &sha1_ctx, 2,
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen i_error("pop3_migration: Failed to read header for msg %u: %s",
57e1fdc2f8f2bf1c6fcd9523f93459404c2359c8Timo Sirainenstatic unsigned int get_cache_idx(struct mail *mail)
57e1fdc2f8f2bf1c6fcd9523f93459404c2359c8Timo Sirainen struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(mail->box);
57e1fdc2f8f2bf1c6fcd9523f93459404c2359c8Timo Sirainen mbox->cache_field.name = "pop3-migration.hdr";
57e1fdc2f8f2bf1c6fcd9523f93459404c2359c8Timo Sirainen mbox->cache_field.type = MAIL_CACHE_FIELD_FIXED_SIZE;
57e1fdc2f8f2bf1c6fcd9523f93459404c2359c8Timo Sirainen mbox->cache_field.field_size = SHA1_RESULTLEN;
57e1fdc2f8f2bf1c6fcd9523f93459404c2359c8Timo Sirainen mail_cache_register_fields(mail->box->cache, &mbox->cache_field, 1);
9625595c47c665f5aee57ebfcb1fcbe9ad1bf3a0Martti Rannanjärviget_hdr_sha1(struct mail *mail, unsigned char sha1_r[STATIC_ARRAY SHA1_RESULTLEN])
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen if (mail_get_hdr_stream(mail, &hdr_size, &input) < 0) {
a58963a8bdac0438a31abc03711e8870b4dfa4f4Timo Sirainen errstr = mailbox_get_last_error(mail->box, &error);
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen i_error("pop3_migration: Failed to get header for msg %u: %s",
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainen if (pop3_migration_get_hdr_sha1(mail->seq, input,
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen struct index_mail *imail = (struct index_mail *)mail;
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen index_mail_cache_add_idx(imail, get_cache_idx(mail),
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen /* The empty "end of headers" line is missing. Either this means that
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen the headers ended unexpectedly (which is ok) or that the remote
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen server is buggy. Some servers have problems with
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen 1) header line continuations that contain only whitespace and
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen 2) headers that have no ":". The header gets truncated when such
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen line is reached.
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen At least Oracle IMS IMAP FETCH BODY[HEADER] handles 1) by not
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen returning the whitespace line and 2) by returning the line but
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen truncating the rest. POP3 TOP instead returns the entire header.
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen This causes the IMAP and POP3 hashes not to match.
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainen If there's LF+CR+CR+LF in the middle of headers, Courier IMAP's
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainen FETCH BODY[HEADER] stops after that, but Courier POP3's TOP doesn't.
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen So we'll try to avoid this by falling back to full FETCH BODY[]
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen (and/or RETR) and we'll parse the header ourself from it. This
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen should work around any similar bugs in all IMAP/POP3 servers. */
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen if (mail_get_stream(mail, &hdr_size, NULL, &input) < 0) {
a58963a8bdac0438a31abc03711e8870b4dfa4f4Timo Sirainen errstr = mailbox_get_last_error(mail->box, &error);
31fd39a3a3d544b1a8afb9aef07f180d0d40fda2Timo Sirainen i_error("pop3_migration: Failed to get body for msg %u: %s",
e18e90938ffd9e31c796c405404be0b7dcd5c807Timo Sirainen return pop3_migration_get_hdr_sha1(mail->seq, input,
57e1fdc2f8f2bf1c6fcd9523f93459404c2359c8Timo Sirainenget_cached_hdr_sha1(struct mail *mail, buffer_t *cache_buf,
9625595c47c665f5aee57ebfcb1fcbe9ad1bf3a0Martti Rannanjärvi unsigned char sha1_r[STATIC_ARRAY SHA1_RESULTLEN])
57e1fdc2f8f2bf1c6fcd9523f93459404c2359c8Timo Sirainen struct index_mail *imail = (struct index_mail *)mail;
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen if (index_mail_cache_lookup_field(imail, cache_buf,
57e1fdc2f8f2bf1c6fcd9523f93459404c2359c8Timo Sirainen memcpy(sha1_r, cache_buf->data, cache_buf->used);
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainenstatic struct mailbox *pop3_mailbox_alloc(struct mail_storage *storage)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct pop3_migration_mail_storage *mstorage =
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ns = mail_namespace_find(storage->user->namespaces,
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen return mailbox_alloc(ns->list, mstorage->pop3_box_vname,
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen MAILBOX_FLAG_READONLY | MAILBOX_FLAG_POP3_SESSION);
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainenstatic int pop3_map_read(struct mail_storage *storage, struct mailbox *pop3_box)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct pop3_migration_mail_storage *mstorage =
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (array_is_created(&mstorage->pop3_uidl_map)) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* already read these, just reset the imap_uids */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen array_foreach_modifiable(&mstorage->pop3_uidl_map, map)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_error("pop3_migration: Couldn't sync mailbox %s: %s",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen pop3_box->vname, mailbox_get_last_error(pop3_box, NULL));
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx = mailbox_search_init(t, search_args, NULL,
7e8bfb5b0af9606f131fc440e61f3752da335ac9Timo Sirainen /* get the size with LIST instead of RETR */
7e8bfb5b0af9606f131fc440e61f3752da335ac9Timo Sirainen mail->lookup_abort = MAIL_LOOKUP_ABORT_READ_MAIL;
b215322367dbd94df3e2e4bb643b53460e6adc51Timo Sirainen else if (mail_get_physical_size(mail, &size) < 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_error("pop3_migration: Failed to get size for msg %u: %s",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (mail_get_special(mail, MAIL_FETCH_UIDL_BACKEND, &uidl) < 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_error("pop3_migration: Failed to get UIDL for msg %u: %s",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_warning("pop3_migration: UIDL for msg %u is empty",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen map = array_append_space(&mstorage->pop3_uidl_map);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen map->pop3_uidl = p_strdup(storage->pool, uidl);
ba57ea2c696f9e9aae909f073069848876a641f4Timo Sirainen i_error("pop3_migration: Failed to search all POP3 mails: %s",
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainenpop3_map_read_cached_hdr_hashes(struct mailbox_transaction_context *t,
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen ctx = mailbox_search_init(t, search_args, NULL, 0, NULL);
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen cache_buf = buffer_create_dynamic(pool_datastack_create(), SHA1_RESULTLEN);
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen map = array_idx_modifiable_i(msg_map, mail->seq-1);
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen if (get_cached_hdr_sha1(mail, cache_buf, map->hdr_sha1))
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen i_warning("pop3_migration: Failed to search all cached POP3 header hashes: %s - ignoring",
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainenstatic void map_remove_found_seqs(struct mail_search_arg *search_arg,
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen seq_range_array_remove(&search_arg->value.seqset, seq);
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainenmap_read_hdr_hashes(struct mailbox *box, struct array *msg_map, uint32_t seq1)
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen /* get all the cached hashes */
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen mail_search_build_add_seqset(search_args, seq1, array_count_i(msg_map));
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen pop3_map_read_cached_hdr_hashes(t, search_args, msg_map);
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen /* read all the non-cached hashes. doing this in two passes allows
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen us to set wanted_fields=MAIL_FETCH_STREAM_HEADER, which allows
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen prefetching to work without downloading all the headers even
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen for mails that already are cached. */
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen map_remove_found_seqs(search_args->args, msg_map, seq1);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx = mailbox_search_init(t, search_args, NULL,
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen map = array_idx_modifiable_i(msg_map, mail->seq-1);
a58963a8bdac0438a31abc03711e8870b4dfa4f4Timo Sirainen if ((ret = get_hdr_sha1(mail, map->hdr_sha1)) < 0) {
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen i_error("pop3_migration: Failed to search all mail headers: %s",
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainenpop3_map_read_hdr_hashes(struct mail_storage *storage, struct mailbox *pop3_box,
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen struct pop3_migration_mail_storage *mstorage =
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen /* we may be matching against multiple mailboxes.
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen read all the hashes only once. */
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen if (map_read_hdr_hashes(pop3_box, &mstorage->pop3_uidl_map.arr,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box);
b215322367dbd94df3e2e4bb643b53460e6adc51Timo Sirainen struct pop3_migration_mail_storage *mstorage =
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mailbox_get_open_status(box, STATUS_MESSAGES, &status);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_assert(!array_is_created(&mbox->imap_msg_map));
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen p_array_init(&mbox->imap_msg_map, box->pool, status.messages);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx = mailbox_search_init(t, search_args, NULL,
b215322367dbd94df3e2e4bb643b53460e6adc51Timo Sirainen else if (mail_get_physical_size(mail, &psize) < 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_error("pop3_migration: Failed to get psize for imap uid %u: %s",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen map = array_append_space(&mbox->imap_msg_map);
ba57ea2c696f9e9aae909f073069848876a641f4Timo Sirainen i_error("pop3_migration: Failed to search all IMAP mails: %s",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int imap_map_read_hdr_hashes(struct mailbox *box)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box);
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen return map_read_hdr_hashes(box, &mbox->imap_msg_map.arr,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic bool pop3_uidl_assign_by_size(struct mailbox *box)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct pop3_migration_mail_storage *mstorage =
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int i, pop3_count, imap_count, count;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen pop3_map = array_get_modifiable(&mstorage->pop3_uidl_map, &pop3_count);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen imap_map = array_get_modifiable(&mbox->imap_msg_map, &imap_count);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* see if we can match the messages using sizes */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen for (i = 0; i < count; i++) {
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen if (i+1 < count && pop3_map[i].size == pop3_map[i+1].size) {
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen /* two messages with same size, don't trust them */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen imap_map[i].pop3_uidl = pop3_map[i].pop3_uidl;
ba57ea2c696f9e9aae909f073069848876a641f4Timo Sirainen i_debug("pop3_migration: %u/%u mails matched by size", i, count);
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainenpop3_uidl_assign_by_hdr_hash(struct mailbox *box, struct mailbox *pop3_box)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct pop3_migration_mail_storage *mstorage =
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int pop3_idx, imap_idx, pop3_count, imap_count;
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen if (pop3_map_read_hdr_hashes(box->storage, pop3_box, first_seq) < 0 ||
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen array_sort(&mstorage->pop3_uidl_map, pop3_uidl_map_hdr_cmp);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen array_sort(&mbox->imap_msg_map, imap_msg_map_hdr_cmp);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen pop3_map = array_get_modifiable(&mstorage->pop3_uidl_map, &pop3_count);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen imap_map = array_get_modifiable(&mbox->imap_msg_map, &imap_count);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen while (pop3_idx < pop3_count && imap_idx < imap_count) {
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen if (!pop3_map[pop3_idx].common.hdr_sha1_set ||
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen if (!imap_map[imap_idx].common.hdr_sha1_set ||
9abc6ac61e70b809f7e1c352c7a3ad1081994d2eTimo Sirainen ret = memcmp(pop3_map[pop3_idx].common.hdr_sha1,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen else if (ret > 0)
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen pop3_map[pop3_idx].imap_uid = imap_map[imap_idx].uid;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen for (pop3_idx = 0; pop3_idx < pop3_count; pop3_idx++) {
a58963a8bdac0438a31abc03711e8870b4dfa4f4Timo Sirainen /* matched */
a58963a8bdac0438a31abc03711e8870b4dfa4f4Timo Sirainen } else if (!pop3_map[pop3_idx].common.hdr_sha1_set) {
a58963a8bdac0438a31abc03711e8870b4dfa4f4Timo Sirainen /* we treated this mail as expunged - ignore */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (missing_uids_count > 0 && !mstorage->all_mailboxes) {
af99ca825f4b7674ec6dd0269bbca665775205aaTimo Sirainen str_printfa(str, "pop3_migration: %u POP3 messages have no "
af99ca825f4b7674ec6dd0269bbca665775205aaTimo Sirainen "matching IMAP messages (first POP3 msg %u UIDL %s)",
e9d659ad49a3cf2190606a62289c86347608bffaTimo Sirainen if (imap_count + missing_uids_count == pop3_count) {
e9d659ad49a3cf2190606a62289c86347608bffaTimo Sirainen str_append(str, " - all IMAP messages were found "
e9d659ad49a3cf2190606a62289c86347608bffaTimo Sirainen "(POP3 contains more than IMAP INBOX - you may want to set pop3_migration_all_mailboxes=yes)");
af99ca825f4b7674ec6dd0269bbca665775205aaTimo Sirainen i_error("%s - set pop3_migration_ignore_missing_uidls=yes to continue anyway",
ba57ea2c696f9e9aae909f073069848876a641f4Timo Sirainen i_debug("pop3_migration: %u mails matched by headers", pop3_count);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen array_sort(&mstorage->pop3_uidl_map, pop3_uidl_map_pop3_seq_cmp);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen array_sort(&mbox->imap_msg_map, imap_msg_map_uid_cmp);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int pop3_migration_uidl_sync(struct mailbox *box)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen struct pop3_migration_mail_storage *mstorage =
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen unsigned int i, count;
0e7d5ff38f28d8c85e197a031bbb66b322ff89e6Timo Sirainen /* the POP3 server isn't connected to yet. handle all IMAP traffic
0e7d5ff38f28d8c85e197a031bbb66b322ff89e6Timo Sirainen first before connecting, so POP3 server won't disconnect us due to
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* everything wasn't assigned, figure out the rest with
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen header hashes */
1c3dc4c08ced3948f52c3c6c171ed77310b2cbfdTimo Sirainen if (pop3_uidl_assign_by_hdr_hash(box, pop3_box) < 0) {
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen /* see if the POP3 UIDL order is the same as IMAP UID order */
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen pop3_map = array_get(&mstorage->pop3_uidl_map, &count);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen for (i = 0; i < count; i++) {
5407a86a03963261bf5ba8793b8c97879ad2e224Timo Sirainenstatic int pop3_migration_uidl_sync_if_needed(struct mailbox *box)
5407a86a03963261bf5ba8793b8c97879ad2e224Timo Sirainen struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box);
5407a86a03963261bf5ba8793b8c97879ad2e224Timo Sirainen mail_storage_set_error(box->storage, MAIL_ERROR_TEMP,
5407a86a03963261bf5ba8793b8c97879ad2e224Timo Sirainen "POP3 UIDLs couldn't be synced");
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenpop3_migration_get_special(struct mail *_mail, enum mail_fetch_field field,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const char **value_r)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct mail_private *mail = (struct mail_private *)_mail;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen union mail_module_context *mmail = POP3_MIGRATION_MAIL_CONTEXT(mail);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(_mail->box);
5407a86a03963261bf5ba8793b8c97879ad2e224Timo Sirainen if (pop3_migration_uidl_sync_if_needed(_mail->box) < 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen map = array_bsearch(&mbox->imap_msg_map, &map_key,
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen *value_r = t_strdup_printf("%u", map->pop3_seq);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen /* not found from POP3 server, fallback to default */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return mmail->super.get_special(_mail, field, value_r);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic void pop3_migration_mail_allocated(struct mail *_mail)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct pop3_migration_mail_storage *mstorage =
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct mail_private *mail = (struct mail_private *)_mail;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen (!mstorage->all_mailboxes && !_mail->box->inbox_user)) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* assigns UIDLs only for INBOX */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ns = mail_namespace_find(_mail->box->storage->user->namespaces,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (ns == mailbox_get_namespace(_mail->box)) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* we're accessing the pop3-migration namespace itself */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mmail = p_new(mail->pool, union mail_module_context, 1);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MODULE_CONTEXT_SET_SELF(mail, pop3_migration_mail_module, mmail);
5407a86a03963261bf5ba8793b8c97879ad2e224Timo Sirainenpop3_migration_mailbox_search_init(struct mailbox_transaction_context *t,
5407a86a03963261bf5ba8793b8c97879ad2e224Timo Sirainen struct mailbox_header_lookup_ctx *wanted_headers)
5407a86a03963261bf5ba8793b8c97879ad2e224Timo Sirainen struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(t->box);
d430698b6d9d47ff9136ee137cc58a1c657baeddTimo Sirainen struct pop3_migration_mail_storage *mstorage =
5407a86a03963261bf5ba8793b8c97879ad2e224Timo Sirainen if ((wanted_fields & (MAIL_FETCH_UIDL_BACKEND |
d430698b6d9d47ff9136ee137cc58a1c657baeddTimo Sirainen (mstorage->all_mailboxes || t->box->inbox_user)) {
5407a86a03963261bf5ba8793b8c97879ad2e224Timo Sirainen /* Start POP3 UIDL syncing before the search, so we'll do it
5407a86a03963261bf5ba8793b8c97879ad2e224Timo Sirainen before we start sending any FETCH BODY[]s to IMAP. It
5407a86a03963261bf5ba8793b8c97879ad2e224Timo Sirainen shouldn't matter much, except this works around a bug in
5407a86a03963261bf5ba8793b8c97879ad2e224Timo Sirainen Yahoo IMAP where it sometimes breaks its state when doing
5407a86a03963261bf5ba8793b8c97879ad2e224Timo Sirainen a FETCH BODY[] followed by FETCH BODY[HEADER].. */
5407a86a03963261bf5ba8793b8c97879ad2e224Timo Sirainen (void)pop3_migration_uidl_sync_if_needed(t->box);
5407a86a03963261bf5ba8793b8c97879ad2e224Timo Sirainen return mbox->module_ctx.super.search_init(t, args, sort_program,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic void pop3_migration_mailbox_allocated(struct mailbox *box)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mbox = p_new(box->pool, struct pop3_migration_mailbox, 1);
5407a86a03963261bf5ba8793b8c97879ad2e224Timo Sirainen v->search_init = pop3_migration_mailbox_search_init;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MODULE_CONTEXT_SET(box, pop3_migration_storage_module, mbox);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic void pop3_migration_mail_storage_destroy(struct mail_storage *storage)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct pop3_migration_mail_storage *mstorage =
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (array_is_created(&mstorage->pop3_uidl_map))
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic void pop3_migration_mail_storage_created(struct mail_storage *storage)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct mail_storage_vfuncs *v = storage->vlast;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen pop3_box_vname = mail_user_plugin_getenv(storage->user,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "pop3_migration_mailbox");
ba57ea2c696f9e9aae909f073069848876a641f4Timo Sirainen i_debug("pop3_migration: No pop3_migration_mailbox setting - disabled");
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mstorage = p_new(storage->pool, struct pop3_migration_mail_storage, 1);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen v->destroy = pop3_migration_mail_storage_destroy;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mstorage->pop3_box_vname = p_strdup(storage->pool, pop3_box_vname);
71748cca1bacd74451fd228db5536828bdfeb190Baofeng Wang "pop3_migration_all_mailboxes");
71748cca1bacd74451fd228db5536828bdfeb190Baofeng Wang "pop3_migration_ignore_missing_uidls");
71748cca1bacd74451fd228db5536828bdfeb190Baofeng Wang "pop3_migration_skip_size_check");
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen MODULE_CONTEXT_SET(storage, pop3_migration_storage_module, mstorage);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic struct mail_storage_hooks pop3_migration_mail_storage_hooks = {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen .mail_allocated = pop3_migration_mail_allocated,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen .mailbox_allocated = pop3_migration_mailbox_allocated,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen .mail_storage_created = pop3_migration_mail_storage_created
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenvoid pop3_migration_plugin_init(struct module *module)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_storage_hooks_add(module, &pop3_migration_mail_storage_hooks);