pop3-migration-plugin.c revision b215322367dbd94df3e2e4bb643b53460e6adc51
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2007-2016 Dovecot authors, see the included COPYING file */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "lib.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "array.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "istream.h"
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen#include "istream-header-filter.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "sha1.h"
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen#include "message-size.h"
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen#include "message-header-parser.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "mail-namespace.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "mail-search-build.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "mail-storage-private.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "pop3-migration-plugin.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#define POP3_MIGRATION_CONTEXT(obj) \
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen MODULE_CONTEXT(obj, pop3_migration_storage_module)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen#define POP3_MIGRATION_MAIL_CONTEXT(obj) \
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen MODULE_CONTEXT(obj, pop3_migration_mail_module)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainenstruct pop3_uidl_map {
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen uint32_t pop3_seq;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen uint32_t imap_uid;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen /* UIDL */
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen const char *pop3_uidl;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen /* LIST size */
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen uoff_t size;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen /* sha1(TOP 0) - set only when needed */
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen unsigned char hdr_sha1[SHA1_RESULTLEN];
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen unsigned int hdr_sha1_set:1;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen};
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainenstruct imap_msg_map {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen uint32_t uid, pop3_seq;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen uoff_t psize;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen const char *pop3_uidl;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen /* sha1(header) - set only when needed */
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen unsigned char hdr_sha1[SHA1_RESULTLEN];
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen unsigned int hdr_sha1_set:1;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen};
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstruct pop3_migration_mail_storage {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen union mail_storage_module_context module_ctx;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen const char *pop3_box_vname;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen ARRAY(struct pop3_uidl_map) pop3_uidl_map;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen unsigned int all_mailboxes:1;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen unsigned int pop3_all_hdr_sha1_set:1;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen unsigned int ignore_missing_uidls:1;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen unsigned int skip_size_check:1;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen};
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainenstruct pop3_migration_mailbox {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen union mailbox_module_context module_ctx;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen ARRAY(struct imap_msg_map) imap_msg_map;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen unsigned int first_unfound_idx;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen unsigned int uidl_synced:1;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen unsigned int uidl_sync_failed:1;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen unsigned int uidl_ordered:1;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen};
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen/* NOTE: these headers must be sorted */
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenstatic const char *hdr_hash_skip_headers[] = {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen "Content-Length",
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen "Return-Path", /* Yahoo IMAP has Return-Path, Yahoo POP3 doesn't */
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen "Status",
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen "X-IMAP",
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen "X-IMAPbase",
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen "X-Keywords",
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen "X-Message-Flag",
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen "X-Status",
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen "X-UID",
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen "X-UIDL"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen};
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenconst char *pop3_migration_plugin_version = DOVECOT_ABI_VERSION;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(pop3_migration_storage_module,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen &mail_storage_module_register);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(pop3_migration_mail_module,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen &mail_module_register);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainenstatic int imap_msg_map_uid_cmp(const struct imap_msg_map *map1,
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen const struct imap_msg_map *map2)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (map1->uid < map2->uid)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return -1;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (map1->uid > map2->uid)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return 1;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return 0;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
f70a4526fa8eaf5faafca01800faf590a4eaedb0Timo Sirainen
4d2211dac61c615c5bdfd501ea54d46c89d41b0fTimo Sirainenstatic int pop3_uidl_map_pop3_seq_cmp(const struct pop3_uidl_map *map1,
4d2211dac61c615c5bdfd501ea54d46c89d41b0fTimo Sirainen const struct pop3_uidl_map *map2)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (map1->pop3_seq < map2->pop3_seq)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return -1;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (map1->pop3_seq > map2->pop3_seq)
12ea3aeeb61df84632f04f86f47902a3750d61f8Timo Sirainen return 1;
12ea3aeeb61df84632f04f86f47902a3750d61f8Timo Sirainen return 0;
12ea3aeeb61df84632f04f86f47902a3750d61f8Timo Sirainen}
63f36c2b47217fc2dc4ed49cfc1907311d5ed366Timo Sirainen
f70a4526fa8eaf5faafca01800faf590a4eaedb0Timo Sirainenstatic int pop3_uidl_map_hdr_cmp(const struct pop3_uidl_map *map1,
4d2211dac61c615c5bdfd501ea54d46c89d41b0fTimo Sirainen const struct pop3_uidl_map *map2)
4d2211dac61c615c5bdfd501ea54d46c89d41b0fTimo Sirainen{
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen return memcmp(map1->hdr_sha1, map2->hdr_sha1, sizeof(map1->hdr_sha1));
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenstatic int imap_msg_map_hdr_cmp(const struct imap_msg_map *map1,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen const struct imap_msg_map *map2)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return memcmp(map1->hdr_sha1, map2->hdr_sha1, sizeof(map1->hdr_sha1));
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstruct pop3_hdr_context {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen bool have_eoh;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen bool stop;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen};
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic void
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenpop3_header_filter_callback(struct header_filter_istream *input ATTR_UNUSED,
26550dc10a8c2057d09894c4e3f9dc12b124e90cTimo Sirainen struct message_header_line *hdr,
26550dc10a8c2057d09894c4e3f9dc12b124e90cTimo Sirainen bool *matched, struct pop3_hdr_context *ctx)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (hdr == NULL)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (hdr->eoh) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen ctx->have_eoh = TRUE;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (ctx->stop) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* matched is handled differently for eoh by
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen istream-header-filter. a design bug I guess.. */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen *matched = FALSE;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen } else {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (strspn(hdr->name, "\r") == hdr->name_len) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* CR+CR+LF - some servers stop the header processing
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen here while others don't. To make sure they can be
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen matched correctly we want to stop here entirely. */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen ctx->stop = TRUE;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (ctx->stop)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen *matched = TRUE;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenint pop3_migration_get_hdr_sha1(uint32_t mail_seq, struct istream *input,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen uoff_t hdr_size,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen unsigned char sha1_r[SHA1_RESULTLEN],
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen bool *have_eoh_r)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct istream *input2;
dcd50ecbfe796bd76f2d63483c534cc0e4e94164Timo Sirainen const unsigned char *data, *p;
dcd50ecbfe796bd76f2d63483c534cc0e4e94164Timo Sirainen size_t size, idx;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct sha1_ctxt sha1_ctx;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct pop3_hdr_context hdr_ctx;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen memset(&hdr_ctx, 0, sizeof(hdr_ctx));
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen input2 = i_stream_create_limit(input, hdr_size);
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen /* hide headers that might change or be different in IMAP vs. POP3 */
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen input = i_stream_create_header_filter(input2,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen hdr_hash_skip_headers,
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen N_ELEMENTS(hdr_hash_skip_headers),
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen pop3_header_filter_callback, &hdr_ctx);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_stream_unref(&input2);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen sha1_init(&sha1_ctx);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen while (i_stream_read_data(input, &data, &size, 0) > 0) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* if there are NULs in header, replace them with 0x80
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen character. This is done by at least Dovecot IMAP and also
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen POP3 with outlook-no-nuls workaround. */
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen while ((p = memchr(data, '\0', size)) != NULL) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen idx = p - data;
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen sha1_loop(&sha1_ctx, data, idx);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen sha1_loop(&sha1_ctx, "\x80", 1);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_assert(size > idx);
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen data += idx + 1;
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen size -= idx + 1;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen sha1_loop(&sha1_ctx, data, size);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_stream_skip(input, size);
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (input->stream_errno != 0) {
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen i_error("pop3_migration: Failed to read header for msg %u: %s",
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen mail_seq, i_stream_get_error(input));
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_stream_unref(&input);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen return -1;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen sha1_result(&sha1_ctx, sha1_r);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_stream_unref(&input);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen *have_eoh_r = hdr_ctx.have_eoh;
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen return 0;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainenstatic int
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainenget_hdr_sha1(struct mail *mail, unsigned char sha1_r[SHA1_RESULTLEN])
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen struct istream *input;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen struct message_size hdr_size;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen bool have_eoh;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen if (mail_get_hdr_stream(mail, &hdr_size, &input) < 0) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_error("pop3_migration: Failed to get header for msg %u: %s",
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen mail->seq, mailbox_get_last_error(mail->box, NULL));
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return -1;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen }
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen if (pop3_migration_get_hdr_sha1(mail->seq, input,
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen hdr_size.physical_size,
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen sha1_r, &have_eoh) < 0)
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen return -1;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen if (have_eoh)
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen return 0;
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen /* The empty "end of headers" line is missing. Either this means that
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen the headers ended unexpectedly (which is ok) or that the remote
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen server is buggy. Some servers have problems with
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen 1) header line continuations that contain only whitespace and
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen 2) headers that have no ":". The header gets truncated when such
b87436ebb957a9eb182be72ba00e2c8eae59a2e4Timo Sirainen line is reached.
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen At least Oracle IMS IMAP FETCH BODY[HEADER] handles 1) by not
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen returning the whitespace line and 2) by returning the line but
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen truncating the rest. POP3 TOP instead returns the entire header.
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen This causes the IMAP and POP3 hashes not to match.
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen
4981827cb5e32cf767b7b0e3070137e6b36f42afTimo Sirainen If there's LF+CR+CR+LF in the middle of headers, Courier IMAP's
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen FETCH BODY[HEADER] stops after that, but Courier POP3's TOP doesn't.
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen So we'll try to avoid this by falling back to full FETCH BODY[]
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen (and/or RETR) and we'll parse the header ourself from it. This
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen should work around any similar bugs in all IMAP/POP3 servers. */
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen if (mail_get_stream(mail, &hdr_size, NULL, &input) < 0) {
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_error("pop3_migration: Failed to get body for msg %u: %s",
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen mail->seq, mailbox_get_last_error(mail->box, NULL));
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen return -1;
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen }
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen return pop3_migration_get_hdr_sha1(mail->seq, input,
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen hdr_size.physical_size,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen sha1_r, &have_eoh);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
2ebeb22b9a8a8bb7fbe2f2e2908478a220792b87Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic struct mailbox *pop3_mailbox_alloc(struct mail_storage *storage)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen{
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen struct pop3_migration_mail_storage *mstorage =
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen POP3_MIGRATION_CONTEXT(storage);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct mail_namespace *ns;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
cf942dce0253075911a96cff323b5f30eb654ae0Timo Sirainen ns = mail_namespace_find(storage->user->namespaces,
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen mstorage->pop3_box_vname);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_assert(ns != NULL);
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen return mailbox_alloc(ns->list, mstorage->pop3_box_vname,
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen MAILBOX_FLAG_READONLY | MAILBOX_FLAG_POP3_SESSION);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen}
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenstatic int pop3_map_read(struct mail_storage *storage, struct mailbox *pop3_box)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen{
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen struct pop3_migration_mail_storage *mstorage =
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen POP3_MIGRATION_CONTEXT(storage);
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen struct mailbox_transaction_context *t;
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen struct mail_search_args *search_args;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen struct mail_search_context *ctx;
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen struct mail *mail;
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen struct pop3_uidl_map *map;
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen const char *uidl;
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen uoff_t size = (uoff_t)-1;
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen int ret = 0;
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen if (array_is_created(&mstorage->pop3_uidl_map)) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen /* already read these, just reset the imap_uids */
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen array_foreach_modifiable(&mstorage->pop3_uidl_map, map)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen map->imap_uid = 0;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen return 0;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen i_array_init(&mstorage->pop3_uidl_map, 128);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (mailbox_sync(pop3_box, 0) < 0) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen i_error("pop3_migration: Couldn't sync mailbox %s: %s",
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen pop3_box->vname, mailbox_get_last_error(pop3_box, NULL));
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen return -1;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen t = mailbox_transaction_begin(pop3_box, 0);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen search_args = mail_search_build_init();
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen mail_search_build_add_all(search_args);
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen ctx = mailbox_search_init(t, search_args, NULL,
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen mstorage->skip_size_check ? 0 :
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen MAIL_FETCH_PHYSICAL_SIZE, NULL);
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen mail_search_args_unref(&search_args);
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen while (mailbox_search_next(ctx, &mail)) {
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen /* get the size with LIST instead of RETR */
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen mail->lookup_abort = MAIL_LOOKUP_ABORT_READ_MAIL;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (mstorage->skip_size_check)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen ;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen else if (mail_get_physical_size(mail, &size) < 0) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen i_error("pop3_migration: Failed to get size for msg %u: %s",
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen mail->seq,
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen mailbox_get_last_error(pop3_box, NULL));
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen ret = -1;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen break;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (mail_get_special(mail, MAIL_FETCH_UIDL_BACKEND, &uidl) < 0) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen i_error("pop3_migration: Failed to get UIDL for msg %u: %s",
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen mail->seq,
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen mailbox_get_last_error(pop3_box, NULL));
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen ret = -1;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen break;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (*uidl == '\0') {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen i_warning("pop3_migration: UIDL for msg %u is empty",
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen mail->seq);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen continue;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen map = array_append_space(&mstorage->pop3_uidl_map);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen map->pop3_seq = mail->seq;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen map->pop3_uidl = p_strdup(storage->pool, uidl);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen map->size = size;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen if (mailbox_search_deinit(&ctx) < 0) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen i_error("pop3_migration: Failed to search all POP3 mails: %s",
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen mailbox_get_last_error(pop3_box, NULL));
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen ret = -1;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen (void)mailbox_transaction_commit(&t);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen return ret;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen}
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenstatic int
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainenpop3_map_read_hdr_hashes(struct mail_storage *storage, struct mailbox *pop3_box,
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen unsigned first_seq)
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen{
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen struct pop3_migration_mail_storage *mstorage =
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen POP3_MIGRATION_CONTEXT(storage);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen struct mailbox_transaction_context *t;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen struct mail_search_args *search_args;
c1563f46bd9ae4ad20f37f003220443a6224fb9fTimo Sirainen struct mail_search_context *ctx;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen struct mail *mail;
c1563f46bd9ae4ad20f37f003220443a6224fb9fTimo Sirainen struct pop3_uidl_map *map;
c1563f46bd9ae4ad20f37f003220443a6224fb9fTimo Sirainen int ret = 0;
c1563f46bd9ae4ad20f37f003220443a6224fb9fTimo Sirainen
c1563f46bd9ae4ad20f37f003220443a6224fb9fTimo Sirainen if (mstorage->pop3_all_hdr_sha1_set)
3091db1d26fed038841a3a81dd5ff507d0b375ceTimo Sirainen return 0;
c1563f46bd9ae4ad20f37f003220443a6224fb9fTimo Sirainen if (mstorage->all_mailboxes) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen /* we may be matching against multiple mailboxes.
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen read all the hashes only once. */
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen first_seq = 1;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen }
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen t = mailbox_transaction_begin(pop3_box, 0);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen search_args = mail_search_build_init();
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen mail_search_build_add_seqset(search_args, first_seq,
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen array_count(&mstorage->pop3_uidl_map)+1);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen ctx = mailbox_search_init(t, search_args, NULL,
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen MAIL_FETCH_STREAM_HEADER, NULL);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen mail_search_args_unref(&search_args);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen while (mailbox_search_next(ctx, &mail)) {
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen map = array_idx_modifiable(&mstorage->pop3_uidl_map,
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen mail->seq-1);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (get_hdr_sha1(mail, map->hdr_sha1) < 0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen ret = -1;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen else
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen map->hdr_sha1_set = TRUE;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (mailbox_search_deinit(&ctx) < 0) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_error("pop3_migration: Failed to search all POP3 mail hashes: %s",
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen mailbox_get_last_error(pop3_box, NULL));
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen ret = -1;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen }
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen (void)mailbox_transaction_commit(&t);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (ret == 0 && first_seq == 1)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen mstorage->pop3_all_hdr_sha1_set = TRUE;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return ret;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen}
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainenstatic int imap_map_read(struct mailbox *box)
e3aeeb634245e80d4f643f8d2eea11d6b72336d8Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct pop3_migration_mail_storage *mstorage =
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen POP3_MIGRATION_CONTEXT(box->storage);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen struct mailbox_status status;
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen struct mailbox_transaction_context *t;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen struct mail_search_args *search_args;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct mail_search_context *ctx;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct mail *mail;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct imap_msg_map *map;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen uoff_t psize = (uoff_t)-1;
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen int ret = 0;
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen mailbox_get_open_status(box, STATUS_MESSAGES, &status);
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen i_assert(!array_is_created(&mbox->imap_msg_map));
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen p_array_init(&mbox->imap_msg_map, box->pool, status.messages);
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen t = mailbox_transaction_begin(box, 0);
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen search_args = mail_search_build_init();
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen mail_search_build_add_all(search_args);
370c4117f854357c9ea86495fb3b4e8769b6db07Timo Sirainen ctx = mailbox_search_init(t, search_args, NULL,
370c4117f854357c9ea86495fb3b4e8769b6db07Timo Sirainen mstorage->skip_size_check ? 0 :
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen MAIL_FETCH_PHYSICAL_SIZE, NULL);
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen mail_search_args_unref(&search_args);
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen while (mailbox_search_next(ctx, &mail)) {
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen if (mstorage->skip_size_check)
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen ;
c4b376dd6e0c423006d7ac83a39253bcaf8e7c47Timo Sirainen else if (mail_get_physical_size(mail, &psize) < 0) {
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen i_error("pop3_migration: Failed to get psize for imap uid %u: %s",
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen mail->uid,
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen mailbox_get_last_error(box, NULL));
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen ret = -1;
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen break;
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen }
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen map = array_append_space(&mbox->imap_msg_map);
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen map->uid = mail->uid;
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen map->psize = psize;
63f36c2b47217fc2dc4ed49cfc1907311d5ed366Timo Sirainen }
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen if (mailbox_search_deinit(&ctx) < 0) {
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen i_error("pop3_migration: Failed to search all IMAP mails: %s",
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen mailbox_get_last_error(box, NULL));
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen ret = -1;
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen }
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen (void)mailbox_transaction_commit(&t);
63110c906fdb5b4e8c870e76fa3f244dac4b043dTimo Sirainen return ret;
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen}
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainenstatic int imap_map_read_hdr_hashes(struct mailbox *box)
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen{
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box);
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen struct mailbox_transaction_context *t;
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen struct mail_search_args *search_args;
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen struct mail_search_context *ctx;
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen struct mail *mail;
63110c906fdb5b4e8c870e76fa3f244dac4b043dTimo Sirainen struct imap_msg_map *map;
63110c906fdb5b4e8c870e76fa3f244dac4b043dTimo Sirainen int ret = 0;
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen t = mailbox_transaction_begin(box, 0);
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen search_args = mail_search_build_init();
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen mail_search_build_add_seqset(search_args, mbox->first_unfound_idx+1,
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen array_count(&mbox->imap_msg_map)+1);
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen ctx = mailbox_search_init(t, search_args, NULL,
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen MAIL_FETCH_STREAM_HEADER, NULL);
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen mail_search_args_unref(&search_args);
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen while (mailbox_search_next(ctx, &mail)) {
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen map = array_idx_modifiable(&mbox->imap_msg_map, mail->seq-1);
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen if (get_hdr_sha1(mail, map->hdr_sha1) < 0)
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen ret = -1;
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen else
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen map->hdr_sha1_set = TRUE;
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen }
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen
6e22333f2cc2562d9bebd93803f70e7ac987f66aTimo Sirainen if (mailbox_search_deinit(&ctx) < 0) {
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen i_error("pop3_migration: Failed to search all IMAP mail hashes: %s",
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen mailbox_get_last_error(box, NULL));
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen ret = -1;
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen }
75bf6f49f6b8ee403f26a609d5c0c726a3262c54Timo Sirainen (void)mailbox_transaction_commit(&t);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen return ret;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen}
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainenstatic bool pop3_uidl_assign_by_size(struct mailbox *box)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen{
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen struct pop3_migration_mail_storage *mstorage =
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen POP3_MIGRATION_CONTEXT(box->storage);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen struct pop3_uidl_map *pop3_map;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen struct imap_msg_map *imap_map;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen unsigned int i, pop3_count, imap_count, count;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen pop3_map = array_get_modifiable(&mstorage->pop3_uidl_map, &pop3_count);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen imap_map = array_get_modifiable(&mbox->imap_msg_map, &imap_count);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen count = I_MIN(pop3_count, imap_count);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen /* see if we can match the messages using sizes */
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen for (i = 0; i < count; i++) {
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (pop3_map[i].size != imap_map[i].psize)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen break;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (i+1 < count && pop3_map[i].size == pop3_map[i+1].size) {
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen /* two messages with same size, don't trust them */
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen break;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen }
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen pop3_map[i].imap_uid = imap_map[i].uid;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen imap_map[i].pop3_uidl = pop3_map[i].pop3_uidl;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen imap_map[i].pop3_seq = pop3_map[i].pop3_seq;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen }
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen mbox->first_unfound_idx = i;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (box->storage->user->mail_debug)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen i_debug("pop3_migration: %u/%u mails matched by size", i, count);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen return i == count;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen}
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainenstatic int
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainenpop3_uidl_assign_by_hdr_hash(struct mailbox *box, struct mailbox *pop3_box)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen{
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen struct pop3_migration_mail_storage *mstorage =
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen POP3_MIGRATION_CONTEXT(box->storage);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct pop3_uidl_map *pop3_map;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen struct imap_msg_map *imap_map;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen unsigned int pop3_idx, imap_idx, pop3_count, imap_count;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen unsigned int first_seq, missing_uids_count;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen uint32_t first_missing_idx = (uint32_t)-1;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen int ret;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen first_seq = mbox->first_unfound_idx+1;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (pop3_map_read_hdr_hashes(box->storage, pop3_box, first_seq) < 0 ||
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen imap_map_read_hdr_hashes(box) < 0)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen return -1;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen array_sort(&mstorage->pop3_uidl_map, pop3_uidl_map_hdr_cmp);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen array_sort(&mbox->imap_msg_map, imap_msg_map_hdr_cmp);
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen
29e82a14501731bef8c41a1b27fc415d634fa01dTimo Sirainen pop3_map = array_get_modifiable(&mstorage->pop3_uidl_map, &pop3_count);
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen imap_map = array_get_modifiable(&mbox->imap_msg_map, &imap_count);
a443e5aaf632257bfd1e7aa9b3c42c09512bbe43Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen pop3_idx = imap_idx = 0;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen while (pop3_idx < pop3_count && imap_idx < imap_count) {
e726bf74fcc8d24f4c9d0d83217b3db4314d9d1fTimo Sirainen if (!pop3_map[pop3_idx].hdr_sha1_set ||
e726bf74fcc8d24f4c9d0d83217b3db4314d9d1fTimo Sirainen pop3_map[pop3_idx].imap_uid != 0) {
e726bf74fcc8d24f4c9d0d83217b3db4314d9d1fTimo Sirainen pop3_idx++;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen continue;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen }
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen if (!imap_map[imap_idx].hdr_sha1_set ||
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen imap_map[imap_idx].pop3_uidl != NULL) {
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen imap_idx++;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen continue;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen }
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen ret = memcmp(pop3_map[pop3_idx].hdr_sha1,
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen imap_map[imap_idx].hdr_sha1,
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen sizeof(pop3_map[pop3_idx].hdr_sha1));
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen if (ret < 0)
e726bf74fcc8d24f4c9d0d83217b3db4314d9d1fTimo Sirainen pop3_idx++;
e726bf74fcc8d24f4c9d0d83217b3db4314d9d1fTimo Sirainen else if (ret > 0)
e726bf74fcc8d24f4c9d0d83217b3db4314d9d1fTimo Sirainen imap_idx++;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen else {
pop3_map[pop3_idx].imap_uid = imap_map[imap_idx].uid;
imap_map[imap_idx].pop3_uidl =
pop3_map[pop3_idx].pop3_uidl;
imap_map[imap_idx].pop3_seq =
pop3_map[pop3_idx].pop3_seq;
}
}
missing_uids_count = 0;
for (pop3_idx = 0; pop3_idx < pop3_count; pop3_idx++) {
if (pop3_map[pop3_idx].imap_uid == 0) {
if (first_missing_idx == (uint32_t)-1)
first_missing_idx = pop3_map[pop3_idx].pop3_seq;
missing_uids_count++;
}
}
if (missing_uids_count > 0 && !mstorage->all_mailboxes) {
if (!mstorage->ignore_missing_uidls) {
i_error("pop3_migration: %u POP3 messages have no "
"matching IMAP messages (set "
"pop3_migration_ignore_missing_uidls=yes "
"to continue anyway)", missing_uids_count);
return -1;
}
i_warning("pop3_migration: %u POP3 messages have no "
"matching IMAP messages (first POP3 msg %u UIDL %s)",
missing_uids_count,
pop3_map[first_missing_idx].pop3_seq,
pop3_map[first_missing_idx].pop3_uidl);
} else if (box->storage->user->mail_debug) {
i_debug("pop3_migration: %u mails matched by headers", pop3_count);
}
array_sort(&mstorage->pop3_uidl_map, pop3_uidl_map_pop3_seq_cmp);
array_sort(&mbox->imap_msg_map, imap_msg_map_uid_cmp);
return 0;
}
static int pop3_migration_uidl_sync(struct mailbox *box)
{
struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box);
struct pop3_migration_mail_storage *mstorage =
POP3_MIGRATION_CONTEXT(box->storage);
struct mailbox *pop3_box;
const struct pop3_uidl_map *pop3_map;
unsigned int i, count;
uint32_t prev_uid;
if (mbox->uidl_synced)
return 0;
pop3_box = pop3_mailbox_alloc(box->storage);
/* the POP3 server isn't connected to yet. handle all IMAP traffic
first before connecting, so POP3 server won't disconnect us due to
idling. */
if (imap_map_read(box) < 0 ||
pop3_map_read(box->storage, pop3_box) < 0) {
mailbox_free(&pop3_box);
return -1;
}
if (!pop3_uidl_assign_by_size(box)) {
/* everything wasn't assigned, figure out the rest with
header hashes */
if (pop3_uidl_assign_by_hdr_hash(box, pop3_box) < 0) {
mailbox_free(&pop3_box);
return -1;
}
}
/* see if the POP3 UIDL order is the same as IMAP UID order */
mbox->uidl_ordered = TRUE;
pop3_map = array_get(&mstorage->pop3_uidl_map, &count);
prev_uid = 0;
for (i = 0; i < count; i++) {
if (pop3_map[i].imap_uid == 0)
continue;
if (prev_uid > pop3_map[i].imap_uid) {
mbox->uidl_ordered = FALSE;
break;
}
prev_uid = pop3_map[i].imap_uid;
}
mbox->uidl_synced = TRUE;
mailbox_free(&pop3_box);
return 0;
}
static int
pop3_migration_get_special(struct mail *_mail, enum mail_fetch_field field,
const char **value_r)
{
struct mail_private *mail = (struct mail_private *)_mail;
union mail_module_context *mmail = POP3_MIGRATION_MAIL_CONTEXT(mail);
struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(_mail->box);
struct imap_msg_map map_key, *map;
if (field == MAIL_FETCH_UIDL_BACKEND ||
field == MAIL_FETCH_POP3_ORDER) {
if (mbox->uidl_sync_failed ||
pop3_migration_uidl_sync(_mail->box) < 0) {
mbox->uidl_sync_failed = TRUE;
mail_storage_set_error(_mail->box->storage,
MAIL_ERROR_TEMP,
"POP3 UIDLs couldn't be synced");
return -1;
}
memset(&map_key, 0, sizeof(map_key));
map_key.uid = _mail->uid;
map = array_bsearch(&mbox->imap_msg_map, &map_key,
imap_msg_map_uid_cmp);
if (map != NULL && map->pop3_uidl != NULL) {
if (field == MAIL_FETCH_UIDL_BACKEND)
*value_r = map->pop3_uidl;
else
*value_r = t_strdup_printf("%u", map->pop3_seq);
return 0;
}
/* not found from POP3 server, fallback to default */
}
return mmail->super.get_special(_mail, field, value_r);
}
static void pop3_migration_mail_allocated(struct mail *_mail)
{
struct pop3_migration_mail_storage *mstorage =
POP3_MIGRATION_CONTEXT(_mail->box->storage);
struct mail_private *mail = (struct mail_private *)_mail;
struct mail_vfuncs *v = mail->vlast;
union mail_module_context *mmail;
struct mail_namespace *ns;
if (mstorage == NULL ||
(!mstorage->all_mailboxes && !_mail->box->inbox_user)) {
/* assigns UIDLs only for INBOX */
return;
}
ns = mail_namespace_find(_mail->box->storage->user->namespaces,
mstorage->pop3_box_vname);
if (ns == mailbox_get_namespace(_mail->box)) {
/* we're accessing the pop3-migration namespace itself */
return;
}
mmail = p_new(mail->pool, union mail_module_context, 1);
mmail->super = *v;
mail->vlast = &mmail->super;
v->get_special = pop3_migration_get_special;
MODULE_CONTEXT_SET_SELF(mail, pop3_migration_mail_module, mmail);
}
static void pop3_migration_mailbox_allocated(struct mailbox *box)
{
struct mailbox_vfuncs *v = box->vlast;
struct pop3_migration_mailbox *mbox;
mbox = p_new(box->pool, struct pop3_migration_mailbox, 1);
mbox->module_ctx.super = *v;
box->vlast = &mbox->module_ctx.super;
MODULE_CONTEXT_SET(box, pop3_migration_storage_module, mbox);
}
static void pop3_migration_mail_storage_destroy(struct mail_storage *storage)
{
struct pop3_migration_mail_storage *mstorage =
POP3_MIGRATION_CONTEXT(storage);
if (array_is_created(&mstorage->pop3_uidl_map))
array_free(&mstorage->pop3_uidl_map);
mstorage->module_ctx.super.destroy(storage);
}
static void pop3_migration_mail_storage_created(struct mail_storage *storage)
{
struct pop3_migration_mail_storage *mstorage;
struct mail_storage_vfuncs *v = storage->vlast;
const char *pop3_box_vname;
pop3_box_vname = mail_user_plugin_getenv(storage->user,
"pop3_migration_mailbox");
if (pop3_box_vname == NULL) {
if (storage->user->mail_debug)
i_debug("pop3_migration: No pop3_migration_mailbox setting - disabled");
return;
}
mstorage = p_new(storage->pool, struct pop3_migration_mail_storage, 1);
mstorage->module_ctx.super = *v;
storage->vlast = &mstorage->module_ctx.super;
v->destroy = pop3_migration_mail_storage_destroy;
mstorage->pop3_box_vname = p_strdup(storage->pool, pop3_box_vname);
mstorage->all_mailboxes =
mail_user_plugin_getenv(storage->user,
"pop3_migration_all_mailboxes") != NULL;
mstorage->ignore_missing_uidls =
mail_user_plugin_getenv(storage->user,
"pop3_migration_ignore_missing_uidls") != NULL;
mstorage->skip_size_check =
mail_user_plugin_getenv(storage->user,
"pop3_migration_skip_size_check") != NULL;
MODULE_CONTEXT_SET(storage, pop3_migration_storage_module, mstorage);
}
static struct mail_storage_hooks pop3_migration_mail_storage_hooks = {
.mail_allocated = pop3_migration_mail_allocated,
.mailbox_allocated = pop3_migration_mailbox_allocated,
.mail_storage_created = pop3_migration_mail_storage_created
};
void pop3_migration_plugin_init(struct module *module)
{
mail_storage_hooks_add(module, &pop3_migration_mail_storage_hooks);
}
void pop3_migration_plugin_deinit(void)
{
mail_storage_hooks_remove(&pop3_migration_mail_storage_hooks);
}