command and library to send email
git clone http://git/xemail
#!/usr/bin/env node
const VERSION = '1.0.0'
const fs = require('fs')
const { Readable } = require('stream')
const path = require('path')
let [args,argv] = require('./lib/args.js')(process.argv.slice(2), {
boolean: [
'h', 'help', 'utc', 'q', 'quiet', 'v', 'version',
'no-date', 'raw', 'k', 'reject-unauthorized',
]
})
if (argv.has('help') || argv.has('h')) {
usage()
return process.exit(0)
}
if (argv.has('version') || argv.has('v')) {
console.log(VERSION)
return process.exit(0)
}
let subject = (argv.get('subject') ?? []).concat(argv.get('u') ?? [])[0]
let server = (argv.get('server') ?? []).concat(argv.get('s') ?? [])[0]
let to = (argv.get('to') ?? []).concat(argv.get('t') ?? [])
let from = (argv.get('from') ?? []).concat(argv.get('f') ?? [])[0]
let msgLines = (argv.get('msg') ?? []).concat(argv.get('m') ?? [])
let keyFile = (argv.get('key') ?? [])[0]
let certFile = (argv.get('cert') ?? [])[0]
let caFile = (argv.get('ca') ?? [])[0]
let key = null, cert = null, ca = null
if (keyFile !== undefined) key = fs.readFileSync(keyFile)
if (certFile !== undefined) cert = fs.readFileSync(certFile)
if (caFile !== undefined) cert = fs.readFileSync(caFile)
let m = null
let port = (argv.get('port') ?? []).concat(argv.get('p') ?? [])[0]
if (m = /^([^:]+):(\d+)$/.exec(server)) {
server = m[1]
port = m[2]
}
let missing = []
if (to.length === 0) missing.push('--to')
if (from === undefined) missing.push('--from')
if (missing.length > 0) {
usage()
console.error(`missing mandatory arguments: ${missing.join(', ')}\n`)
return process.exit(1)
}
if (server === undefined) {
let m = /(?:<[^@]+@([^>]+)\s*>|[^@]+@(\S+))/.exec(from)
server = m[1] ?? m[2]
}
let date = (argv.get('date') ?? []).concat(argv.get('d') ?? [])[0]
let hasDateHeader = false
let headers = [ 'To: ' + to.join(','), 'From: ' + from ]
;(argv.get('header') ?? []).concat(argv.get('H') ?? []).forEach(h => {
hasDateHeader &&= /^date\s*:/i.test(h)
headers.push(h)
})
if (argv.has('no-date')) date = undefined
else if (!hasDateHeader && date === undefined) date = new Date
if (date !== undefined && argv.has('utc')) {
headers.push('Date: ' + date.toUTCString())
} else if (date !== undefined) {
headers.push('Date: ' + date.toString().replace(/\([^)]*\)/,''))
}
if (subject !== undefined) headers.push('Subject: ' + subject)
let rawHeaders = argv.has('raw')
let inStream = process.stdin
if (msgLines.length > 0 && rawHeaders) {
inStream = Readable.from(msgLines.join('\r\n') + '\r\n')
} else if (msgLines.length > 0) {
inStream = Readable.from(headers.join('\r\n') + '\r\n\r\n' + msgLines.join('\r\n') + '\r\n')
} else if (args.length > 0 && args[0] !== '-') {
inStream = fs.createReadStream(args[0])
if (!rawHeaders) inStream.push(headers.join('\r\n') + '\r\n\r\n')
} else if (!rawHeaders) {
inStream.push(headers.join('\r\n') + '\r\n\r\n')
}
let opts = {
to, from, server, port,
rejectUnauthorized: (argv.get('reject-unauthorized') ?? []).concat(argv.get('k') ?? [])[0],
key, cert, ca,
data: inStream
}
require('./')(opts, (err,sent,code) => {
if (err) {
console.error(err)
process.exit(1)
} else if (!sent) {
console.error('message not sent. code: ' + code)
process.exit(1)
} else if (!argv.has('quiet') && !argv.has('q')) {
console.error('email sent successfully')
}
})
function getCmd() { return path.basename(process.argv[1]) }
function usage() {
console.error(`
usage: ${getCmd()} {OPTIONS...} (MSG_FILE|-)
Send an email message with the contents of MSG_FILE, stdin ("-"), -m, or --msg.
-f FROM, --from=FROM
-t TO, --to=TO
Required arguments where FROM and TO are each email addresses.
You can also format the addresses in the form "DISPLAY <ADDR@HOST>"
which will set the appropriate header to set a display name
different from the email address.
Specify multiple recipients by using --to multiple times.
-s SERVER, --server=SERVER
-s SERVER:PORT, --server=SERVER:PORT
-p PORT, --port PORT
Connect to an smtp SERVER on PORT (default 25).
If a SERVER isn't explicitly given, it is derived from FROM.
-m MSG_LINE, --msg MSG_LINE
Instead of reading from MSG_FILE or stdin, set lines of content from arguments.
Each -m or --msg will add one line to the message body.
-H FIELD:VALUE, --header=FIELD:VALUE
Add "FIELD: VALUE" to the set of headers.
--date=DATE
Alias for --header=Date:DATE
--no-date
Do not automatically set a Date header.
--utc
Use UTC time for automatically sent Date headers.
--raw
Do not send any headers and do not encode the payload.
MSG_FILE is responsible for setting headers and encoding.
--cert=CERTFILE --key=KEYFILE
Use a client-side tls certificate for the connection.
--ca=CAFILE
Use a custom certificate to verify the remote server.
-k, --reject-unauthorized
Do not validate the SSL certificate presented by the server.
-v, --version
Print the versinn of this software. (${VERSION})
--help -h
Show this message.
`.trim().replace(/^ {4}/mg,'') + '\n')
}