all-in-one smtp+imap with minimal setup
git clone http://git.nthia.dev/nthmail
const { TLSSocket, createSecureContext } = require('tls')
const { Readable, Duplex } = require('stream')
module.exports = TLS
function TLS(opts) {
Duplex.call(this)
if (!opts) opts = {}
this._tlsClear = null
this._tlsCipher = null
this._tlsNext = null
this._tlsBuffer = null
this._tlsRead = false
this._streamNext = null
this._streamBuffer = null
this._streamRead = false
this._streamFlush = false
this._pair = opts.pair
this._tls = Object.assign({}, opts.tls ?? {})
if (opts.key) this._tls.key = opts.key
if (opts.cert) this._tls.cert = opts.cert
if (opts.ca) this._tls.ca = opts.ca
this._isServer = true
if (opts.isServer !== undefined) this._isServer = opts.isServer
}
TLS.prototype = Object.create(Duplex.prototype)
TLS.prototype._read = function (n) {
if (this._streamNext) {
var buf = this._streamBuffer
var f = this._streamNext
this._streamBuffer = null
this._streamNext = null
this.push(buf)
if (this._streamFlush) this.push(null)
f()
} else {
this._streamRead = true
}
}
TLS.prototype._write = function (buf, enc, next) {
if (this._tlsCipher) {
if (this._tlsRead) {
this._tlsRead = false
this._tlsCipher.push(buf)
next()
} else {
this._tlsBuffer = buf
this._tlsNext = next
}
} else this._writeBuf(buf, enc, next)
}
TLS.prototype._destroy = function (err, next) {
if (this._tlsClear) this._tlsClear.destroy()
}
TLS.prototype._startTLS = function () {
var self = this
self._tlsCipher = new Duplex({
read(n) {
if (self._tlsNext) {
this.push(self._tlsBuffer)
var f = self._tlsNext
self._tlsNext = null
self._tlsBuffer = null
f()
} else {
self._tlsRead = true
}
},
write(buf, enc, next) {
if (self._streamRead) {
self._streamRead = false
self.push(buf)
next()
} else {
self._streamNext = next
self._streamBuffer = buf
}
},
flush(next) {
if (self._streamNext) {
self._streamFlush = true
} else {
self.push(null)
next()
}
},
})
var secureContext = createSecureContext(self._tls)
self._tlsClear = new TLSSocket(self._tlsCipher, {
pair: this._pair,
isServer: self._isServer,
requestCert: true,
rejectUnauthorized: false,
secureContext,
})
self._tlsClear.on('secure', () => {
self.emit('secure', self._tlsClear)
let cert = self._tlsClear.getPeerCertificate()
if (cert) self.emit('certificate', cert)
})
self._tlsClear.on('readable', () => {
;(function next(err) {
if (err) return
var buf = self._tlsClear.read()
if (buf === null) return
self._writeBuf(buf, null, next)
})()
})
}
TLS.prototype._push = function (buf) {
if (this._tlsClear && buf === null) {
this._tlsClear.end()
} else if (this._tlsClear) {
this._tlsClear.write(buf)
} else {
this.push(buf)
}
}
TLS.prototype._end = function () {
if (this._tlsClear) {
this._tlsClear.end()
} else {
this.push(null)
}
}