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} */ 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'); }