pop3-migration-plugin.c revision 06fda713b84e857dbc3e80f401a54085c9b0ed16
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik/* Copyright (c) 2007-2016 Dovecot authors, see the included COPYING file */
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik MODULE_CONTEXT(obj, pop3_migration_storage_module)
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnik MODULE_CONTEXT(obj, pop3_migration_mail_module)
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik /* sha1(header) - set only when needed */
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik /* LIST size */
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik union mail_storage_module_context module_ctx;
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik/* NOTE: these headers must be sorted */
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnikstatic const char *hdr_hash_skip_headers[] = {
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik "Content-Length",
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik "Return-Path", /* Yahoo IMAP has Return-Path, Yahoo POP3 doesn't */
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik "X-IMAPbase",
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik "X-Keywords",
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik "X-Message-Flag",
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik "X-Yahoo-Newman-Property"
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnikconst char *pop3_migration_plugin_version = DOVECOT_ABI_VERSION;
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnikstatic MODULE_CONTEXT_DEFINE_INIT(pop3_migration_storage_module,
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnikstatic MODULE_CONTEXT_DEFINE_INIT(pop3_migration_mail_module,
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnikstatic int imap_msg_map_uid_cmp(const struct imap_msg_map *map1,
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnikstatic int pop3_uidl_map_pop3_seq_cmp(const struct pop3_uidl_map *map1,
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnikstatic int pop3_uidl_map_hdr_cmp(const struct pop3_uidl_map *map1,
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik return memcmp(map1->common.hdr_sha1, map2->common.hdr_sha1,
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnikstatic int imap_msg_map_hdr_cmp(const struct imap_msg_map *map1,
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik return memcmp(map1->common.hdr_sha1, map2->common.hdr_sha1,
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnikstatic bool header_name_is_valid(const char *name)
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik unsigned int i;
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik if ((uint8_t)name[i] <= 0x20 || name[i] >= 0x7f)
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnikpop3_header_filter_callback(struct header_filter_istream *input ATTR_UNUSED,
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik bool *matched, struct pop3_hdr_context *ctx)
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik /* matched is handled differently for eoh by
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik istream-header-filter. a design bug I guess.. */
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik if (strspn(hdr->name, "\r") == hdr->name_len) {
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik /* CR+CR+LF - some servers stop the header processing
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik here while others don't. To make sure they can be
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik matched correctly we want to stop here entirely. */
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik else if (!header_name_is_valid(hdr->name)) {
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik /* Yahoo IMAP drops headers with invalid names, while
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik Yahoo POP3 preserves them. Drop them all. */
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnikint pop3_migration_get_hdr_sha1(uint32_t mail_seq, struct istream *input,
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik const unsigned char *data;
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik input2 = i_stream_create_limit(input, hdr_size);
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik /* hide headers that might change or be different in IMAP vs. POP3 */
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik input = i_stream_create_header_filter(input2,
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR,
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik while (i_stream_read_data(input, &data, &size, 0) > 0) {
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik message_header_hash_more(&hash_method_sha1, &sha1_ctx, 2,
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik i_error("pop3_migration: Failed to read header for msg %u: %s",
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnikstatic unsigned int get_cache_idx(struct mail *mail)
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(mail->box);
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik mbox->cache_field.name = "pop3-migration.hdr";
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik mbox->cache_field.type = MAIL_CACHE_FIELD_FIXED_SIZE;
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik mbox->cache_field.field_size = SHA1_RESULTLEN;
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik mail_cache_register_fields(mail->box->cache, &mbox->cache_field, 1);
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnikget_hdr_sha1(struct mail *mail, unsigned char sha1_r[SHA1_RESULTLEN])
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik if (mail_get_hdr_stream(mail, &hdr_size, &input) < 0) {
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik i_error("pop3_migration: Failed to get header for msg %u: %s",
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik mail->seq, mailbox_get_last_error(mail->box, NULL));
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik if (pop3_migration_get_hdr_sha1(mail->seq, input,
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik struct index_mail *imail = (struct index_mail *)mail;
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik index_mail_cache_add_idx(imail, get_cache_idx(mail),
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik /* The empty "end of headers" line is missing. Either this means that
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik the headers ended unexpectedly (which is ok) or that the remote
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik server is buggy. Some servers have problems with
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik 1) header line continuations that contain only whitespace and
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik 2) headers that have no ":". The header gets truncated when such
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik line is reached.
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik At least Oracle IMS IMAP FETCH BODY[HEADER] handles 1) by not
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik returning the whitespace line and 2) by returning the line but
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik truncating the rest. POP3 TOP instead returns the entire header.
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik This causes the IMAP and POP3 hashes not to match.
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik If there's LF+CR+CR+LF in the middle of headers, Courier IMAP's
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik FETCH BODY[HEADER] stops after that, but Courier POP3's TOP doesn't.
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik So we'll try to avoid this by falling back to full FETCH BODY[]
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik (and/or RETR) and we'll parse the header ourself from it. This
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik should work around any similar bugs in all IMAP/POP3 servers. */
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik if (mail_get_stream(mail, &hdr_size, NULL, &input) < 0) {
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik i_error("pop3_migration: Failed to get body for msg %u: %s",
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik mail->seq, mailbox_get_last_error(mail->box, NULL));
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik return pop3_migration_get_hdr_sha1(mail->seq, input,
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnikget_cached_hdr_sha1(struct mail *mail, buffer_t *cache_buf,
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik struct index_mail *imail = (struct index_mail *)mail;
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik if (index_mail_cache_lookup_field(imail, cache_buf,
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik memcpy(sha1_r, cache_buf->data, cache_buf->used);
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnikstatic struct mailbox *pop3_mailbox_alloc(struct mail_storage *storage)
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik struct pop3_migration_mail_storage *mstorage =
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik ns = mail_namespace_find(storage->user->namespaces,
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik return mailbox_alloc(ns->list, mstorage->pop3_box_vname,
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik MAILBOX_FLAG_READONLY | MAILBOX_FLAG_POP3_SESSION);
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnikstatic int pop3_map_read(struct mail_storage *storage, struct mailbox *pop3_box)
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik struct pop3_migration_mail_storage *mstorage =
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik if (array_is_created(&mstorage->pop3_uidl_map)) {
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik /* already read these, just reset the imap_uids */
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik array_foreach_modifiable(&mstorage->pop3_uidl_map, map)
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik i_array_init(&mstorage->pop3_uidl_map, 128);
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik i_error("pop3_migration: Couldn't sync mailbox %s: %s",
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik pop3_box->vname, mailbox_get_last_error(pop3_box, NULL));
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik ctx = mailbox_search_init(t, search_args, NULL,
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik /* get the size with LIST instead of RETR */
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik mail->lookup_abort = MAIL_LOOKUP_ABORT_READ_MAIL;
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik else if (mail_get_physical_size(mail, &size) < 0) {
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik i_error("pop3_migration: Failed to get size for msg %u: %s",
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik if (mail_get_special(mail, MAIL_FETCH_UIDL_BACKEND, &uidl) < 0) {
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik i_error("pop3_migration: Failed to get UIDL for msg %u: %s",
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik i_warning("pop3_migration: UIDL for msg %u is empty",
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik map = array_append_space(&mstorage->pop3_uidl_map);
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik map->pop3_uidl = p_strdup(storage->pool, uidl);
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik i_error("pop3_migration: Failed to search all POP3 mails: %s",
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnikpop3_map_read_cached_hdr_hashes(struct mailbox_transaction_context *t,
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik ctx = mailbox_search_init(t, search_args, NULL, 0, NULL);
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik cache_buf = buffer_create_dynamic(pool_datastack_create(), SHA1_RESULTLEN);
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik map = array_idx_modifiable_i(msg_map, mail->seq-1);
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik if (get_cached_hdr_sha1(mail, cache_buf, map->hdr_sha1))
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik i_warning("pop3_migration: Failed to search all cached POP3 header hashes: %s - ignoring",
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnikstatic void map_remove_found_seqs(struct mail_search_arg *search_arg,
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik uint32_t seq, count = array_count_i(msg_map);
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik i_assert(search_arg->type == SEARCH_SEQSET);
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik seq_range_array_remove(&search_arg->value.seqset, seq);
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnikmap_read_hdr_hashes(struct mailbox *box, struct array *msg_map, uint32_t seq1)
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik /* get all the cached hashes */
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik mail_search_build_add_seqset(search_args, seq1, array_count_i(msg_map));
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik pop3_map_read_cached_hdr_hashes(t, search_args, msg_map);
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik /* read all the non-cached hashes. doing this in two passes allows
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik us to set wanted_fields=MAIL_FETCH_STREAM_HEADER, which allows
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik prefetching to work without downloading all the headers even
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik for mails that already are cached. */
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik map_remove_found_seqs(search_args->args, msg_map, seq1);
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik ctx = mailbox_search_init(t, search_args, NULL,
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik map = array_idx_modifiable_i(msg_map, mail->seq-1);
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik if (get_hdr_sha1(mail, map->hdr_sha1) < 0) {
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik i_error("pop3_migration: Failed to search all mail headers: %s",
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnikpop3_map_read_hdr_hashes(struct mail_storage *storage, struct mailbox *pop3_box,
cb8c24707275c5bda7310d67e7f46c75d3ac36eaLukas Slebodnik struct pop3_migration_mail_storage *mstorage =
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnik /* we may be matching against multiple mailboxes.
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnik read all the hashes only once. */
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnik if (map_read_hdr_hashes(pop3_box, &mstorage->pop3_uidl_map.arr,
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnikstatic int imap_map_read(struct mailbox *box)
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnik struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box);
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnik struct pop3_migration_mail_storage *mstorage =
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnik mailbox_get_open_status(box, STATUS_MESSAGES, &status);
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnik i_assert(!array_is_created(&mbox->imap_msg_map));
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnik p_array_init(&mbox->imap_msg_map, box->pool, status.messages);
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnik ctx = mailbox_search_init(t, search_args, NULL,
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnik else if (mail_get_physical_size(mail, &psize) < 0) {
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnik i_error("pop3_migration: Failed to get psize for imap uid %u: %s",
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnik map = array_append_space(&mbox->imap_msg_map);
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnik i_error("pop3_migration: Failed to search all IMAP mails: %s",
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnikstatic int imap_map_read_hdr_hashes(struct mailbox *box)
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnik struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box);
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnik return map_read_hdr_hashes(box, &mbox->imap_msg_map.arr,
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnikstatic bool pop3_uidl_assign_by_size(struct mailbox *box)
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnik struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box);
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnik struct pop3_migration_mail_storage *mstorage =
c3baf4d7c0cbd139d96fd04f6b3c175d2f99de6cLukas Slebodnik unsigned int i, pop3_count, imap_count, count;
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik pop3_map = array_get_modifiable(&mstorage->pop3_uidl_map, &pop3_count);
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik imap_map = array_get_modifiable(&mbox->imap_msg_map, &imap_count);
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik /* see if we can match the messages using sizes */
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik for (i = 0; i < count; i++) {
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik if (i+1 < count && pop3_map[i].size == pop3_map[i+1].size) {
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik /* two messages with same size, don't trust them */
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik imap_map[i].pop3_uidl = pop3_map[i].pop3_uidl;
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik imap_map[i].pop3_seq = pop3_map[i].pop3_seq;
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik i_debug("pop3_migration: %u/%u mails matched by size", i, count);
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnikpop3_uidl_assign_by_hdr_hash(struct mailbox *box, struct mailbox *pop3_box)
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik struct pop3_migration_mail_storage *mstorage =
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box);
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik unsigned int pop3_idx, imap_idx, pop3_count, imap_count;
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik if (pop3_map_read_hdr_hashes(box->storage, pop3_box, first_seq) < 0 ||
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik array_sort(&mstorage->pop3_uidl_map, pop3_uidl_map_hdr_cmp);
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik array_sort(&mbox->imap_msg_map, imap_msg_map_hdr_cmp);
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik pop3_map = array_get_modifiable(&mstorage->pop3_uidl_map, &pop3_count);
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik imap_map = array_get_modifiable(&mbox->imap_msg_map, &imap_count);
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik while (pop3_idx < pop3_count && imap_idx < imap_count) {
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik if (!pop3_map[pop3_idx].common.hdr_sha1_set ||
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik if (!imap_map[imap_idx].common.hdr_sha1_set ||
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik ret = memcmp(pop3_map[pop3_idx].common.hdr_sha1,
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik sizeof(pop3_map[pop3_idx].common.hdr_sha1));
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik else if (ret > 0)
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik pop3_map[pop3_idx].imap_uid = imap_map[imap_idx].uid;
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik for (pop3_idx = 0; pop3_idx < pop3_count; pop3_idx++) {
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik if (missing_uids_count > 0 && !mstorage->all_mailboxes) {
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik str_printfa(str, "pop3_migration: %u POP3 messages have no "
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik "matching IMAP messages (first POP3 msg %u UIDL %s)",
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik if (imap_count + missing_uids_count == pop3_count) {
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik str_append(str, " - all IMAP messages were found "
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik "(POP3 contains more than IMAP INBOX - you may want to set pop3_migration_all_mailboxes=yes)");
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik i_error("%s - set pop3_migration_ignore_missing_uidls=yes to continue anyway",
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik } else if (box->storage->user->mail_debug) {
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik i_debug("pop3_migration: %u mails matched by headers", pop3_count);
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik array_sort(&mstorage->pop3_uidl_map, pop3_uidl_map_pop3_seq_cmp);
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik array_sort(&mbox->imap_msg_map, imap_msg_map_uid_cmp);
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnikstatic int pop3_migration_uidl_sync(struct mailbox *box)
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box);
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik struct pop3_migration_mail_storage *mstorage =
1584db9c0b15ee239bd4e163c4ecaf6909b1b11bPavel Reichl unsigned int i, count;
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik pop3_box = pop3_mailbox_alloc(box->storage);
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik /* the POP3 server isn't connected to yet. handle all IMAP traffic
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik first before connecting, so POP3 server won't disconnect us due to
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik pop3_map_read(box->storage, pop3_box) < 0) {
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik /* everything wasn't assigned, figure out the rest with
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik header hashes */
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik if (pop3_uidl_assign_by_hdr_hash(box, pop3_box) < 0) {
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik /* see if the POP3 UIDL order is the same as IMAP UID order */
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik pop3_map = array_get(&mstorage->pop3_uidl_map, &count);
1584db9c0b15ee239bd4e163c4ecaf6909b1b11bPavel Reichl for (i = 0; i < count; i++) {
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnikstatic int pop3_migration_uidl_sync_if_needed(struct mailbox *box)
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box);
1584db9c0b15ee239bd4e163c4ecaf6909b1b11bPavel Reichl mail_storage_set_error(box->storage, MAIL_ERROR_TEMP,
089db891b8a7a94b5666e8cffb1d7b359d6aeb6eLukas Slebodnik "POP3 UIDLs couldn't be synced");
b28f5fb097e06a97a45e0ae348e506d9d1432cc8Lukas Slebodnikpop3_migration_get_special(struct mail *_mail, enum mail_fetch_field field,
b28f5fb097e06a97a45e0ae348e506d9d1432cc8Lukas Slebodnik struct mail_private *mail = (struct mail_private *)_mail;
b28f5fb097e06a97a45e0ae348e506d9d1432cc8Lukas Slebodnik union mail_module_context *mmail = POP3_MIGRATION_MAIL_CONTEXT(mail);
b28f5fb097e06a97a45e0ae348e506d9d1432cc8Lukas Slebodnik struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(_mail->box);
b28f5fb097e06a97a45e0ae348e506d9d1432cc8Lukas Slebodnik if (pop3_migration_uidl_sync_if_needed(_mail->box) < 0)
b28f5fb097e06a97a45e0ae348e506d9d1432cc8Lukas Slebodnik map = array_bsearch(&mbox->imap_msg_map, &map_key,
b28f5fb097e06a97a45e0ae348e506d9d1432cc8Lukas Slebodnik if (map != NULL && map->pop3_uidl != NULL) {
b28f5fb097e06a97a45e0ae348e506d9d1432cc8Lukas Slebodnik *value_r = t_strdup_printf("%u", map->pop3_seq);
b28f5fb097e06a97a45e0ae348e506d9d1432cc8Lukas Slebodnik /* not found from POP3 server, fallback to default */
b28f5fb097e06a97a45e0ae348e506d9d1432cc8Lukas Slebodnik return mmail->super.get_special(_mail, field, value_r);
b28f5fb097e06a97a45e0ae348e506d9d1432cc8Lukas Slebodnikstatic void pop3_migration_mail_allocated(struct mail *_mail)
b28f5fb097e06a97a45e0ae348e506d9d1432cc8Lukas Slebodnik struct pop3_migration_mail_storage *mstorage =
b28f5fb097e06a97a45e0ae348e506d9d1432cc8Lukas Slebodnik POP3_MIGRATION_CONTEXT(_mail->box->storage);
b28f5fb097e06a97a45e0ae348e506d9d1432cc8Lukas Slebodnik struct mail_private *mail = (struct mail_private *)_mail;
static struct mail_search_context *
MAIL_FETCH_POP3_ORDER)) != 0 &&
const char *pop3_box_vname;
void pop3_migration_plugin_deinit(void)