all-in-one smtp+imap with minimal setup
git clone http://git.nthia.dev/nthmail
const { scrypt } = require('crypto')
const { nextTick } = process
module.exports = Login
function Login(opts) {
if (!(this instanceof Login)) return new Login(opts)
this._accounts = new Map
this._names = new Map
;(opts.accounts ?? []).forEach(a => {
this._accounts.set(a.id, a)
;(a.names ?? []).forEach(name => {
this._names.set(name, a)
})
})
this._logins = (opts.logins ?? []).map(l => {
l = Object.assign({}, l)
if (l.fingerprint512 !== undefined) l.fingerprint512 = normfp(l.fingerprint512)
if (typeof l.salt === 'string') l.salt = Buffer.from(l.salt,'hex')
return l
})
this._domains = new Set
;(opts.domains ?? []).forEach(d => this._domains.add(d))
}
Login.prototype.checkSmtp = function (src, dst, accountId) {
let srcName = src.replace(/@.*/,'')
let srcDomain = src.replace(/^[^@]*@/,'')
let dstName = dst.replace(/@.*/,'')
let dstDomain = dst.replace(/^[^@]*@/,'')
if (this._domains.has(srcDomain)) {
if (accountId === undefined || accountId === null) return false
let a = this._accounts[accountId]
if (!a) return false
if (!Array.isArray(a.names)) return false
return a.names.includes(srcName)
}
return this._domains.has(dstDomain)
}
Login.prototype.checkLogin = function (opts, cb) {
let self = this
let fp512 = opts.fingerprint512 !== undefined ? normfp(opts.fingerprint512) : null
;(function next(i) {
if (i >= self._logins.length) return cb(null, null)
let l = self._logins[i]
if (opts.type === 'plain' && l.type === 'scrypt') {
if (opts.password === undefined) return next(i+1)
if (l.username !== opts.username) return next(i+1)
if (l.key === undefined) return next(i+1)
if (l.fingerprint512 !== undefined && fp512 !== l.fingerprint512) return next(i+1)
scrypt(opts.password, l.salt, 64, (err,key) => {
if (err) {
let f = cb
cb = noop
return f(err)
}
if (key.toString('hex') === l.key) {
cb(null, l.account)
} else {
next(i+1)
}
})
} else if (opts.type === 'fingerprint' && l.type === 'fingerprint') {
if (fp512 !== undefined && fp512 === l.fingerprint512) {
cb(null, l.account)
} else {
next(i+1)
}
} else {
next(i+1)
}
})(0)
}
Login.prototype.getAccountFromEmail = function (email) {
let name = email.replace(/@.*/,'')
let domain = email.replace(/^[^@]*@/,'')
if (!this._domains.has(domain)) return null
return this.getAccountFromName(name)
}
Login.prototype.getAccountFromName = function (name) {
return this._names.get(name) ?? null
}
Login.prototype.getAccountFromId = function (id) {
return this._accounts.get(id)
}
Login.prototype.hasDomain = function (domain) {
return this._domains.has(domain)
}
function normfp(x) { return String(x).toLowerCase().replace(/:/g,'') }
function noop() {}