Skip to content

Commit

Permalink
wip: Provide hooks to manage IMAP connection #277
Browse files Browse the repository at this point in the history
  • Loading branch information
cnouguier committed Jun 17, 2024
1 parent 2ee390c commit b2ee7c3
Showing 1 changed file with 103 additions and 16 deletions.
119 changes: 103 additions & 16 deletions lib/hooks/hooks.imap.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import _ from 'lodash'
import path from 'path'
import fs from 'fs'
import { ImapFlow } from 'imapflow'
import makeDebug from 'debug'
import { callOnHookItems } from '../utils.js'
import { callOnHookItems, getStoreFromHook } from '../utils.js'

const debug = makeDebug('krawler:hooks:imap')

Expand Down Expand Up @@ -46,39 +48,124 @@ export function disconnectIMAP (options = {}) {

// List mailboxes
export function listIMAPMailboxes (options = {}) {
async function listMailboxes (item, hook) {
return callOnHookItems(options)(async (item, hook) => {
const client = _.get(hook.data, options.clientPath || 'client')
if (_.isNil(client)) throw new Error('You must provide client parameters to use the \'listIMAP\' hook')
if (_.isNil(client)) throw new Error('You must provide client parameters to use the \'listIMAPMailboxes\' hook')

debug(`[IMAP] listing mailboxes for ${hook.data.id}`)
debug(`[IMAP] ${hook.data.id}: listing mailboxes`)
const mailboxes = await client.list(options)
console.log(mailboxes)
_.set(hook, options.dataPath || 'result.data', mailboxes)
return hook
}
return callOnHookItems(options)(listMailboxes)
})
}

// List mailboxes
// List messages for a given mailbox
export function fetchIMAPMessages (options = {}) {
async function fetchMessage (item, hook) {
return callOnHookItems(options)(async (item, hook) => {
const client = _.get(hook.data, options.clientPath || 'client')
if (_.isNil(client)) throw new Error('You must provide client parameters to use the \'listIMAP\' hook')
if (_.isNil(client)) throw new Error('You must provide client parameters to use the \'fetchIMAPMessages\' hook')

debug(`[IMAP] fetching messages for ${hook.data.id}`)
debug(`[IMAP] ${hook.data.id}: fetching messages`)
let lock = await client.getMailboxLock(options.mailbox)
try {
let messages = []
for await (const message of client.fetch(options.range, options.query, _.omit(options, ['mailbox', 'range', 'query', 'clientPath']))) {
messages.push(message)
}
console.log(messages)
_.set(hook, options.dataPath || 'result.data', messages)
} finally {
// Make sure lock is released, otherwise next `getMailboxLock()` never returns
lock.release();
}
return hook
}
return callOnHookItems(options)(fetchMessage)
})
}

// Downalod message attachment for a given mailbox
export function downloadIMAPAttachments (options = {}) {
return callOnHookItems(options)(async (item, hook) => {
const client = _.get(hook.data, options.clientPath || 'client')
if (_.isNil(client)) throw new Error('You must provide client parameters to use the \'downloadIMAPContent\' hook')
const store = await getStoreFromHook(hook, 'downloadIMAPAttachment', options)

debug(`[IMAP] ${hook.data.id}: download attachments`)
let lock = await client.getMailboxLock(options.mailbox)
try {
let attachment = []
const message = await client.fetchOne(options.range, { bodyStructure: true })
const parts = _.get(message.bodyStructure, 'childNodes')
const filteredParts = _.filter(parts, part => part.type === options.type)
for (const part of filteredParts) {
// see example https://github.com/postalsys/imapflow/blob/master/examples/example.js#L242
let {meta, content} = await client.download(message.seq, part.part, _.omit(options, ['mailbox', 'range', 'part', 'type', 'clientPath']))
if (content) {
let stream = fs.createWriteStream(path.join(store.path, meta.filename))
await new Promise(resolve => {
content.pipe(stream);
stream.once('finish', () => {
debug(`[IMAP] written attachement ${meta.filename}`)
resolve()
})
})
attachment.push(meta.filename)
}
}
if (attachment.length === 1) attachment = attachment[0]
else if (attachment.length === 0) attachment = null
_.set(hook, options.dataPath || 'result.data', attachment)
console.log(hook)
} finally {
// Make sure lock is released, otherwise next `getMailboxLock()` never returns
lock.release()
}
})
}

// Flag messages
export function flagIMAPMessages (options = {}) {
return callOnHookItems(options)(async (item, hook) => {
const client = _.get(hook.data, options.clientPath || 'client')
if (_.isNil(client)) throw new Error('You must provide client parameters to use the \'deleteIMAPMessages\' hook')

debug(`[IMAP] ${hook.data.id}: flag messages`)
let lock = await client.getMailboxLock(options.mailbox)
try {
await client.messageFlagsAdd(options.range, options.flags, _.omit(options, ['mailbox', 'range', 'flags', 'clientPath']))
} finally {
// Make sure lock is released, otherwise next `getMailboxLock()` never returns
lock.release()
}
})
}

// Unflag messages
export function unflagIMAPMessages (options = {}) {
return callOnHookItems(options)(async (item, hook) => {
const client = _.get(hook.data, options.clientPath || 'client')
if (_.isNil(client)) throw new Error('You must provide client parameters to use the \'deleteIMAPMessages\' hook')

debug(`[IMAP] ${hook.data.id}: unflag messages`)
let lock = await client.getMailboxLock(options.mailbox)
try {
await client.messageFlagsRemove(options.range, options.flags, _.omit(options, ['mailbox', 'range', 'flags', 'clientPath']))
} finally {
// Make sure lock is released, otherwise next `getMailboxLock()` never returns
lock.release()
}
})
}

// Delete messages
export function deleteIMAPMessages (options = {}) {
return callOnHookItems(options)(async (item, hook) => {
const client = _.get(hook.data, options.clientPath || 'client')
if (_.isNil(client)) throw new Error('You must provide client parameters to use the \'deleteIMAPMessages\' hook')

debug(`[IMAP] ${hook.data.id}: delete messages`)
let lock = await client.getMailboxLock(options.mailbox)
try {
await client.messageDelete(options.range, _.omit(options, ['mailbox', 'range', 'clientPath']))
} finally {
// Make sure lock is released, otherwise next `getMailboxLock()` never returns
lock.release();
}
})
}

0 comments on commit b2ee7c3

Please sign in to comment.