1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
const { ipcMain } = require('electron');
const { ImapFlow } = require('imapflow');
ipcMain.on('imap:listTree:from', listTreeFrom);
ipcMain.on('imap:listTree:to', listTreeTo);
ipcMain.on('imap:migrate', migrate);
/**
* Connect to server
*
* @param {object} options
*
* @returns {Promise<ImapFlow>}
*/
async function connect (options) {
const client = new ImapFlow({
host: options.server,
port: options.port,
auth: {
user: options.username,
pass: options.password,
},
tls: options.tls,
});
await client.connect();
return client;
}
/**
* Turn fetch response into array
*
* @param {string} range
* @param {object} query
* @param {object} options
*
* @returns {Promise<*[]>}
*/
ImapFlow.prototype.fetchArray = async function (range, query, options = {}) {
const msgsGenerator = await this.fetch(range, query, options);
const msgs = [];
for await (const msg of msgsGenerator) {
msgs.push(msg);
}
return msgs;
};
/**
* @param {IpcMainEvent} event
* @param {object} options
*/
async function listTreeFrom (event, options) {
try {
const client = await connect(options);
event.reply('imap:listTree:from:reply', await client.listTree());
await client.logout();
} catch (e) {
event.reply('imap:from:error', e);
}
}
/**
* @param {IpcMainEvent} event
* @param {object} options
*/
async function listTreeTo (event, options) {
try {
const client = await connect(options);
event.reply('imap:listTree:to:reply', await client.listTree());
await client.logout();
} catch (e) {
event.reply('imap:to:error', e);
}
}
/**
* @param {IpcMainEvent} event
* @param {object} from
* @param {object} to
*/
async function migrate (event, { from, to }) {
const fromClient = await connect(from);
const toClient = await connect(to);
event.reply('imap:migrate:progress', 'Getting folders');
const fromFolders = (await fromClient.list()).filter((folder) => folder.subscribed);
for (const fromFolder of fromFolders) {
event.reply('imap:migrate:progress', `Working on folder "${fromFolder.path}"`);
try {
await toClient.mailboxCreate(fromFolder.path);
} catch (e) {}
const appendCommands = [];
const fromMailbox = await fromClient.getMailboxLock(fromFolder.path);
const toMailbox = await toClient.getMailboxLock(fromFolder.path);
try {
event.reply('imap:migrate:progress', '- Collecting messages from target for comparison');
const toMsgs = await toClient.fetchArray('1:*', { flags: true, envelope: true, source: true });
event.reply('imap:migrate:progress', '- Collecting messages from source');
const fromMsgs = await fromClient.fetchArray('1:*', { flags: true, envelope: true, source: true });
event.reply('imap:migrate:progress', '- Comparing messages');
const msgs = fromMsgs.filter((fromMsg) => !toMsgs.some((toMsg) => Buffer.compare(toMsg.source, fromMsg.source) === 0));
event.reply('imap:migrate:progress', `- Message status: Found ${fromMsgs.length} messages total and ${msgs.length} new messages to migrate`);
await toClient.noop();
for (const msg of msgs) {
appendCommands.push(toClient.append(fromFolder.path, msg.source, Array.from(msg.flags), msg.envelope.date));
}
} finally {
await Promise.all(appendCommands);
event.reply('imap:migrate:progress', '- Done');
fromMailbox.release();
toMailbox.release();
}
}
event.reply('imap:migrate:progress', 'Done');
}
|