import { createTransport , SendMailOptions } from "nodemailer" ; import SMTPTransport = require ( "nodemailer/lib/smtp-transport" ) ; import { decode } from "rfc2047" ; export interface IParsedMBox { body : string ; cc ? : string [ ] ; date ? : string ; from ? : string ; headers ? : Array < { key : string ; value : string ; } > ; messageId ? : string ; subject ? : string ; to ? : string ; raw : string ; } export interface ISMTPOptions { smtpUser : string ; smtpHost : string ; smtpOpts ? : string ; smtpPass : string ; } export async function parseHeadersAndSendMail ( mbox , smtpOptions ) { return await sendMail ( parseMBox ( mbox ) , smtpOptions ) ; } function replaceAll ( input , pattern , replacement ) { return input . split ( pattern ) . join ( replacement ) ; } param { string } mbox returns { IParsedMBox } export function parseMBox ( mbox , gentle ? ) { const headerEnd = mbox . indexOf ( "\n\n" ) ; if ( headerEnd < 0 ) { throw new Error ( "Could not parse mail" ) ; } const headerStart = mbox . startsWith ( "From " ) ? mbox . indexOf ( "\n" ) + 1 : 0 ; const header = mbox . substr ( headerStart , headerEnd - headerStart ) ; const body = mbox . substr ( headerEnd + 2 ) ; let cc ; let date ; let from ; const headers = new Array < { key : string ; value : string ; } > ( ) ; let messageId ; let subject ; let to ; for ( const line of header . split ( /\n(?![ \t])/ ) ) { const colon = line . indexOf ( ": " ) ; if ( colon < 0 ) { throw new Error ( `Failed to parse header line '${ line }` ) ; } const key = line . substr ( 0 , colon ) ; const value = replaceAll ( line . substr ( colon + 2 ) , "\n " , " " ) ; switch ( key . toLowerCase ( ) ) { case "cc" : cc = ( cc || [ ] ) . concat ( value . split ( ", " ) ) ; break ; case "date" : date = value ; break ; case "fcc" : break ; case "from" : from = decode ( value . trim ( ) ) ; break ; case "message-id" : messageId = value ; break ; case "subject" : subject = value ; break ; case "to" : to = value ; break ; default : headers . push ( { key , value } ) ; } } if ( ! gentle && ( ! to || ! subject || ! from ) ) { throw new Error ( `Missing To, Subject and/or From header:\n${ header }` ) ; } return { body , cc , date , from , headers , messageId , raw : mbox , subject , to , } ; } export function parseMBoxMessageIDAndReferences ( parsed ) { const references = [ ] ; const seen = new Set < string > ( ) ; const msgIdRegex = /^\s*<([^>]+)>(\s*|,)(\([^")]*("[^"]*")?\)\s*|\([^)]*\)$)?(<.*)?$/ ; for ( const header of parsed . headers ?? [ ] ) { if ( header . key === "In-Reply-To" || header . key === "References" ) { let value = header . value ; while ( value ) { const match = value . match ( msgIdRegex ) ; if ( ! match ) { if ( value !== undefined && ! value . match ( /^\s*$/ ) ) { throw new Error ( `Error parsing Message-ID '${ value }'` ) ; } break ; } if ( ! seen . has ( match [ 1 ] ) ) { references . push ( match [ 1 ] ) ; seen . add ( match [ 1 ] ) ; } value = match [ 5 ] ; } } } if ( ! parsed . messageId ) { throw new Error ( `No Message-ID found in ${ parsed . raw }` ) ; } const messageID = parsed . messageId . match ( /^<(.*)>$/ ) ; if ( ! messageID ) { throw new Error ( `Unexpected Message-ID format: ${ parsed . messageId }` ) ; } return { messageID : messageID [ 1 ] , references } ; } export async function sendMail ( mail , smtpOptions ) { const transportOpts = { auth : { pass : smtpOptions . smtpPass , user : smtpOptions . smtpUser , } , host : smtpOptions . smtpHost , secure : true , } ; if ( smtpOptions . smtpOpts ) { const smtpOpts = smtpOptions . smtpOpts . replace ( /([ {])([a-zA-Z0-9.]+?) *?:/g , "$1\"$2\":" ) ; Object . assign ( transportOpts , JSON . parse ( smtpOpts ) ) ; } return new Promise < string > ( ( resolve , reject ) => { const transporter = createTransport ( transportOpts ) ; const mailOptions = { envelope : { cc : mail . cc ? mail . cc . join ( ", " ) : undefined , from : mail . from , to : mail . to , } , raw : mail . raw , } ; transporter . sendMail ( mailOptions , ( error , info ) : void => { if ( error ) { reject ( error ) ; } else { resolve ( info . messageId ) ; } } ) ; } ) ; }