pop3-migration-plugin.c revision 049da065aa64c1a5ed46eed6cde7382b011612a9
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen/* Copyright (c) 2007-2012 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)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* LIST size */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* sha1(TOP 0) - set only when needed */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* sha1(header) - set only when needed */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ARRAY_DEFINE(pop3_uidl_map, struct pop3_uidl_map);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ARRAY_DEFINE(imap_msg_map, struct imap_msg_map);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic const char *hdr_hash_skip_headers[] = {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "Content-Length",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "X-IMAPbase",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "X-Keywords",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "X-Message-Flag",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenconst char *pop3_migration_plugin_version = DOVECOT_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,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return memcmp(map1->hdr_sha1, map2->hdr_sha1, sizeof(map1->hdr_sha1));
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int imap_msg_map_hdr_cmp(const struct imap_msg_map *map1,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return memcmp(map1->hdr_sha1, map2->hdr_sha1, sizeof(map1->hdr_sha1));
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int get_hdr_sha1(struct mail *mail, unsigned char sha1[SHA1_RESULTLEN])
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const unsigned char *data;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen if (mail_get_hdr_stream(mail, &hdr_size, &input) < 0) {
049da065aa64c1a5ed46eed6cde7382b011612a9Timo Sirainen i_error("pop3_migration: Failed to get header for msg %u: %s",
049da065aa64c1a5ed46eed6cde7382b011612a9Timo Sirainen mail->seq, mailbox_get_last_error(mail->box, NULL));
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen input2 = i_stream_create_limit(input, hdr_size.physical_size);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* hide headers that might change or be different in IMAP vs. POP3 */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen while (i_stream_read_data(input, &data, &size, 0) > 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_error("pop3_migration: Failed to read header for msg %u: %m",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int pop3_mailbox_open(struct mail_storage *storage)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct pop3_migration_mail_storage *mstorage =
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ns = mail_namespace_find(storage->user->namespaces,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_error("pop3_migration: Namespace not found for mailbox %s",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mstorage->pop3_box = mailbox_alloc(ns->list, mstorage->pop3_box_vname,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int pop3_map_read(struct mail_storage *storage)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct pop3_migration_mail_storage *mstorage =
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct mailbox *pop3_box = mstorage->pop3_box;
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,
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);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int pop3_map_read_hdr_hashes(struct mail_storage *storage,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct pop3_migration_mail_storage *mstorage =
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* we may be matching against multiple mailboxes.
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen read all the hashes only once. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen t = mailbox_transaction_begin(mstorage->pop3_box, 0);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_search_build_add_seqset(search_args, first_seq,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx = mailbox_search_init(t, search_args, NULL,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen map = array_idx_modifiable(&mstorage->pop3_uidl_map,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box);
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,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen 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);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int imap_map_read_hdr_hashes(struct mailbox *box)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_search_build_add_seqset(search_args, mbox->first_unfound_idx+1,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ctx = mailbox_search_init(t, search_args, NULL,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen map = array_idx_modifiable(&mbox->imap_msg_map, mail->seq-1);
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;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic int pop3_uidl_assign_by_hdr_hash(struct mailbox *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;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (pop3_map_read_hdr_hashes(box->storage, 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) {
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++) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (missing_uids_count > 0 && !mstorage->all_mailboxes) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_warning("pop3_migration: %u POP3 messages have no "
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "matching IMAP messages", missing_uids_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;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (pop3_map_read(box->storage) < 0 || imap_map_read(box) < 0)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* everything wasn't assigned, figure out the rest with
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen header hashes */
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++) {
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);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "POP3 UIDLs couldn't be synced");
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);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenstatic void pop3_migration_mailbox_allocated(struct mailbox *box)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mbox = p_new(box->pool, struct pop3_migration_mailbox, 1);
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 Sirainen if (mstorage->module_ctx.super.destroy != NULL)
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");
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);
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);