pop3-migration-plugin.c revision ba57ea2c696f9e9aae909f073069848876a641f4
/* Copyright (c) 2007-2015 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "istream.h"
#include "istream-header-filter.h"
#include "sha1.h"
#include "message-size.h"
#include "message-header-parser.h"
#include "mail-namespace.h"
#include "mail-search-build.h"
#include "mail-storage-private.h"
#include "pop3-migration-plugin.h"
#define POP3_MIGRATION_CONTEXT(obj) \
#define POP3_MIGRATION_MAIL_CONTEXT(obj) \
struct pop3_uidl_map {
/* UIDL */
const char *pop3_uidl;
/* LIST size */
/* sha1(TOP 0) - set only when needed */
unsigned char hdr_sha1[SHA1_RESULTLEN];
unsigned int hdr_sha1_set:1;
};
struct imap_msg_map {
const char *pop3_uidl;
/* sha1(header) - set only when needed */
unsigned char hdr_sha1[SHA1_RESULTLEN];
unsigned int hdr_sha1_set:1;
};
struct pop3_migration_mail_storage {
const char *pop3_box_vname;
unsigned int all_mailboxes:1;
unsigned int pop3_all_hdr_sha1_set:1;
unsigned int ignore_missing_uidls:1;
};
struct pop3_migration_mailbox {
union mailbox_module_context module_ctx;
unsigned int first_unfound_idx;
unsigned int uidl_synced:1;
unsigned int uidl_sync_failed:1;
unsigned int uidl_ordered:1;
};
static const char *hdr_hash_skip_headers[] = {
"Content-Length",
"Status",
"X-IMAP",
"X-IMAPbase",
"X-Keywords",
"X-Message-Flag",
"X-Status",
"X-UID",
"X-UIDL"
};
const char *pop3_migration_plugin_version = DOVECOT_ABI_VERSION;
const struct imap_msg_map *map2)
{
return -1;
return 1;
return 0;
}
const struct pop3_uidl_map *map2)
{
return -1;
return 1;
return 0;
}
const struct pop3_uidl_map *map2)
{
}
const struct imap_msg_map *map2)
{
}
static void
struct message_header_line *hdr,
{
}
static int
{
const unsigned char *data, *p;
*have_eoh_r = FALSE;
/* hide headers that might change or be different in IMAP vs. POP3 */
/* if there are NULs in header, replace them with 0x80
character. This is done by at least Dovecot IMAP and also
POP3 with outlook-no-nuls workaround. */
}
}
if (input->stream_errno != 0) {
i_error("pop3_migration: Failed to read header for msg %u: %s",
return -1;
}
return 0;
}
static int
{
struct message_size hdr_size;
bool have_eoh;
i_error("pop3_migration: Failed to get header for msg %u: %s",
return -1;
}
return -1;
if (have_eoh)
return 0;
/* The empty "end of headers" line is missing. Either this means that
the headers ended unexpectedly (which is ok) or that the remote
server is buggy. Some servers have problems with
1) header line continuations that contain only whitespace and
2) headers that have no ":". The header gets truncated when such
line is reached.
At least Oracle IMS IMAP FETCH BODY[HEADER] handles 1) by not
returning the whitespace line and 2) by returning the line but
truncating the rest. POP3 TOP instead returns the entire header.
This causes the IMAP and POP3 hashes not to match.
So we'll try to avoid this by falling back to full FETCH BODY[]
i_error("pop3_migration: Failed to get body for msg %u: %s",
return -1;
}
}
{
struct pop3_migration_mail_storage *mstorage =
struct mail_namespace *ns;
}
{
struct pop3_migration_mail_storage *mstorage =
struct mailbox_transaction_context *t;
struct mail_search_args *search_args;
struct mail_search_context *ctx;
struct pop3_uidl_map *map;
const char *uidl;
int ret = 0;
/* already read these, just reset the imap_uids */
return 0;
}
if (mailbox_sync(pop3_box, 0) < 0) {
i_error("pop3_migration: Couldn't sync mailbox %s: %s",
return -1;
}
t = mailbox_transaction_begin(pop3_box, 0);
i_error("pop3_migration: Failed to get size for msg %u: %s",
ret = -1;
break;
}
i_error("pop3_migration: Failed to get UIDL for msg %u: %s",
ret = -1;
break;
}
if (*uidl == '\0') {
i_warning("pop3_migration: UIDL for msg %u is empty",
continue;
}
}
if (mailbox_search_deinit(&ctx) < 0) {
i_error("pop3_migration: Failed to search all POP3 mails: %s",
ret = -1;
}
(void)mailbox_transaction_commit(&t);
return ret;
}
static int
unsigned first_seq)
{
struct pop3_migration_mail_storage *mstorage =
struct mailbox_transaction_context *t;
struct mail_search_args *search_args;
struct mail_search_context *ctx;
struct pop3_uidl_map *map;
int ret = 0;
return 0;
if (mstorage->all_mailboxes) {
/* we may be matching against multiple mailboxes.
read all the hashes only once. */
first_seq = 1;
}
t = mailbox_transaction_begin(pop3_box, 0);
ret = -1;
else
}
if (mailbox_search_deinit(&ctx) < 0) {
i_error("pop3_migration: Failed to search all POP3 mail hashes: %s",
ret = -1;
}
(void)mailbox_transaction_commit(&t);
return ret;
}
{
struct mailbox_status status;
struct mailbox_transaction_context *t;
struct mail_search_args *search_args;
struct mail_search_context *ctx;
struct imap_msg_map *map;
int ret = 0;
t = mailbox_transaction_begin(box, 0);
i_error("pop3_migration: Failed to get psize for imap uid %u: %s",
ret = -1;
break;
}
}
if (mailbox_search_deinit(&ctx) < 0) {
i_error("pop3_migration: Failed to search all IMAP mails: %s",
ret = -1;
}
(void)mailbox_transaction_commit(&t);
return ret;
}
{
struct mailbox_transaction_context *t;
struct mail_search_args *search_args;
struct mail_search_context *ctx;
struct imap_msg_map *map;
int ret = 0;
t = mailbox_transaction_begin(box, 0);
ret = -1;
else
}
if (mailbox_search_deinit(&ctx) < 0) {
i_error("pop3_migration: Failed to search all IMAP mail hashes: %s",
ret = -1;
}
(void)mailbox_transaction_commit(&t);
return ret;
}
{
struct pop3_migration_mail_storage *mstorage =
struct pop3_uidl_map *pop3_map;
struct imap_msg_map *imap_map;
/* see if we can match the messages using sizes */
for (i = 0; i < count; i++) {
break;
/* two messages with same size, don't trust them */
break;
}
}
mbox->first_unfound_idx = i;
return i == count;
}
static int
{
struct pop3_migration_mail_storage *mstorage =
struct pop3_uidl_map *pop3_map;
struct imap_msg_map *imap_map;
unsigned int first_seq, missing_uids_count;
int ret;
imap_map_read_hdr_hashes(box) < 0)
return -1;
pop3_idx++;
continue;
}
imap_idx++;
continue;
}
if (ret < 0)
pop3_idx++;
else if (ret > 0)
imap_idx++;
else {
}
}
missing_uids_count = 0;
}
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", missing_uids_count);
}
return 0;
}
{
struct pop3_migration_mail_storage *mstorage =
const struct pop3_uidl_map *pop3_map;
unsigned int i, count;
if (mbox->uidl_synced)
return 0;
/* 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 ||
return -1;
}
if (!pop3_uidl_assign_by_size(box)) {
/* everything wasn't assigned, figure out the rest with
header hashes */
return -1;
}
}
/* see if the POP3 UIDL order is the same as IMAP UID order */
prev_uid = 0;
for (i = 0; i < count; i++) {
continue;
break;
}
}
return 0;
}
static int
const char **value_r)
{
if (field == MAIL_FETCH_UIDL_BACKEND ||
field == MAIL_FETCH_POP3_ORDER) {
if (mbox->uidl_sync_failed ||
"POP3 UIDLs couldn't be synced");
return -1;
}
if (field == MAIL_FETCH_UIDL_BACKEND)
else
return 0;
}
/* not found from POP3 server, fallback to default */
}
}
{
struct pop3_migration_mail_storage *mstorage =
union mail_module_context *mmail;
struct mail_namespace *ns;
/* assigns UIDLs only for INBOX */
return;
}
/* we're accessing the pop3-migration namespace itself */
return;
}
}
{
struct pop3_migration_mailbox *mbox;
}
{
struct pop3_migration_mail_storage *mstorage =
}
{
struct pop3_migration_mail_storage *mstorage;
const char *pop3_box_vname;
"pop3_migration_mailbox");
if (pop3_box_vname == NULL) {
i_debug("pop3_migration: No pop3_migration_mailbox setting - disabled");
return;
}
"pop3_migration_all_mailboxes") != NULL;
"pop3_migration_ignore_missing_uidls") != NULL;
}
static struct mail_storage_hooks pop3_migration_mail_storage_hooks = {
};
{
}
void pop3_migration_plugin_deinit(void)
{
}