2026-09-15 13:00:32 +02:00
"use strict" ;
var _ _createBinding = ( this && this . _ _createBinding ) || ( Object . create ? ( function ( o , m , k , k2 ) {
if ( k2 === undefined ) k2 = k ;
var desc = Object . getOwnPropertyDescriptor ( m , k ) ;
if ( ! desc || ( "get" in desc ? ! m . _ _esModule : desc . writable || desc . configurable ) ) {
desc = { enumerable : true , get : function ( ) { return m [ k ] ; } } ;
}
Object . defineProperty ( o , k2 , desc ) ;
} ) : ( function ( o , m , k , k2 ) {
if ( k2 === undefined ) k2 = k ;
o [ k2 ] = m [ k ] ;
} ) ) ;
var _ _setModuleDefault = ( this && this . _ _setModuleDefault ) || ( Object . create ? ( function ( o , v ) {
Object . defineProperty ( o , "default" , { enumerable : true , value : v } ) ;
} ) : function ( o , v ) {
o [ "default" ] = v ;
} ) ;
var _ _importStar = ( this && this . _ _importStar ) || ( function ( ) {
var ownKeys = function ( o ) {
ownKeys = Object . getOwnPropertyNames || function ( o ) {
var ar = [ ] ;
for ( var k in o ) if ( Object . prototype . hasOwnProperty . call ( o , k ) ) ar [ ar . length ] = k ;
return ar ;
} ;
return ownKeys ( o ) ;
} ;
return function ( mod ) {
if ( mod && mod . _ _esModule ) return mod ;
var result = { } ;
if ( mod != null ) for ( var k = ownKeys ( mod ) , i = 0 ; i < k . length ; i ++ ) if ( k [ i ] !== "default" ) _ _createBinding ( result , mod , k [ i ] ) ;
_ _setModuleDefault ( result , mod ) ;
return result ;
} ;
} ) ( ) ;
var _ _importDefault = ( this && this . _ _importDefault ) || function ( mod ) {
return ( mod && mod . _ _esModule ) ? mod : { "default" : mod } ;
} ;
Object . defineProperty ( exports , "__esModule" , { value : true } ) ;
const index _js _1 = _ _importDefault ( require ( "../smtp-connection/index.js" ) ) ;
const index _js _2 = require ( "../shared/index.js" ) ;
const index _js _3 = _ _importDefault ( require ( "../xoauth2/index.js" ) ) ;
const errors = _ _importStar ( require ( "../errors.js" ) ) ;
const node _events _1 = require ( "node:events" ) ;
2020-02-29 23:01:03 +01:00
/**
* Creates an element for the pool
*
* @constructor
2026-09-15 13:00:32 +02:00
* @param pool SMTPPool instance
2020-02-29 23:01:03 +01:00
*/
2026-09-15 13:00:32 +02:00
class PoolResource extends node _events _1 . EventEmitter {
2020-02-29 23:01:03 +01:00
constructor ( pool ) {
super ( ) ;
this . pool = pool ;
this . options = pool . options ;
this . logger = this . pool . logger ;
if ( this . options . auth ) {
switch ( ( this . options . auth . type || '' ) . toString ( ) . toUpperCase ( ) ) {
case 'OAUTH2' : {
2026-09-15 13:00:32 +02:00
const oauth2 = new index _js _3 . default ( this . options . auth , this . logger ) ;
2025-12-25 10:58:28 +01:00
oauth2 . provisionCallback =
( this . pool . mailer && this . pool . mailer . get ( 'oauth2_provision_cb' ) ) || oauth2 . provisionCallback ;
2020-02-29 23:01:03 +01:00
this . auth = {
type : 'OAUTH2' ,
user : this . options . auth . user ,
oauth2 ,
method : 'XOAUTH2'
} ;
2026-09-15 13:00:32 +02:00
oauth2 . on ( 'token' , ( token ) => this . pool . mailer . emit ( 'token' , token ) ) ;
2020-02-29 23:01:03 +01:00
oauth2 . on ( 'error' , err => this . emit ( 'error' , err ) ) ;
break ;
}
default :
if ( ! this . options . auth . user && ! this . options . auth . pass ) {
break ;
}
this . auth = {
type : ( this . options . auth . type || '' ) . toString ( ) . toUpperCase ( ) || 'LOGIN' ,
user : this . options . auth . user ,
credentials : {
user : this . options . auth . user || '' ,
pass : this . options . auth . pass ,
options : this . options . auth . options
} ,
method : ( this . options . auth . method || '' ) . trim ( ) . toUpperCase ( ) || this . options . authMethod || false
} ;
}
}
this . _connection = false ;
this . _connected = false ;
this . messages = 0 ;
this . available = true ;
}
/**
* Initiates a connection to the SMTP server
*
2026-09-15 13:00:32 +02:00
* @param callback Callback function to run once the connection is established or failed
2020-02-29 23:01:03 +01:00
*/
connect ( callback ) {
this . pool . getSocket ( this . options , ( err , socketOptions ) => {
if ( err ) {
2026-09-15 13:00:32 +02:00
// nothing was connected, so no 'close' event is coming that would free the
// slot this resource holds in the pool, report the failure the way a failed
// login does
this . emit ( 'error' , err ) ;
2020-02-29 23:01:03 +01:00
return callback ( err ) ;
}
let returned = false ;
let options = this . options ;
if ( socketOptions && socketOptions . connection ) {
2026-09-15 13:00:32 +02:00
this . logger . info ( {
tnx : 'proxy' ,
remoteAddress : socketOptions . connection . remoteAddress ,
remotePort : socketOptions . connection . remotePort ,
destHost : options . host || '' ,
destPort : options . port || '' ,
action : 'connected'
} , 'Using proxied socket from %s:%s to %s:%s' , socketOptions . connection . remoteAddress , socketOptions . connection . remotePort , options . host || '' , options . port || '' ) ;
options = Object . assign ( ( 0 , index _js _2 . assign ) ( false , options ) , socketOptions ) ;
2020-02-29 23:01:03 +01:00
}
2026-09-15 13:00:32 +02:00
this . connection = new index _js _1 . default ( options ) ;
2020-02-29 23:01:03 +01:00
this . connection . once ( 'error' , err => {
this . emit ( 'error' , err ) ;
if ( returned ) {
return ;
}
returned = true ;
return callback ( err ) ;
} ) ;
this . connection . once ( 'end' , ( ) => {
this . close ( ) ;
if ( returned ) {
return ;
}
returned = true ;
2026-04-28 12:50:45 +02:00
const timer = setTimeout ( ( ) => {
2020-02-29 23:01:03 +01:00
if ( returned ) {
return ;
}
// still have not returned, this means we have an unexpected connection close
2026-04-28 12:50:45 +02:00
const err = new Error ( 'Unexpected socket close' ) ;
2026-09-15 13:00:32 +02:00
if ( this . connection &&
this . connection . _socket &&
this . connection . _socket . upgrading ) {
2020-02-29 23:01:03 +01:00
// starttls connection errors
2026-02-05 08:49:11 +01:00
err . code = errors . ETLS ;
2020-02-29 23:01:03 +01:00
}
callback ( err ) ;
} , 1000 ) ;
try {
timer . unref ( ) ;
2026-09-15 13:00:32 +02:00
}
catch ( _E ) {
2020-02-29 23:01:03 +01:00
// Ignore. Happens on envs with non-node timer implementation
}
} ) ;
this . connection . connect ( ( ) => {
if ( returned ) {
return ;
}
2020-03-24 13:34:13 +01:00
if ( this . auth && ( this . connection . allowsAuth || options . forceAuth ) ) {
2020-02-29 23:01:03 +01:00
this . connection . login ( this . auth , err => {
if ( returned ) {
return ;
}
returned = true ;
if ( err ) {
this . connection . close ( ) ;
this . emit ( 'error' , err ) ;
return callback ( err ) ;
}
this . _connected = true ;
callback ( null , true ) ;
} ) ;
2026-09-15 13:00:32 +02:00
}
else {
2020-02-29 23:01:03 +01:00
returned = true ;
this . _connected = true ;
return callback ( null , true ) ;
}
} ) ;
} ) ;
}
/**
* Sends an e-mail to be sent using the selected settings
*
2026-09-15 13:00:32 +02:00
* @param mail Mail object
* @param callback Callback function
2020-02-29 23:01:03 +01:00
*/
send ( mail , callback ) {
if ( ! this . _connected ) {
return this . connect ( err => {
if ( err ) {
return callback ( err ) ;
}
return this . send ( mail , callback ) ;
} ) ;
}
2026-04-28 12:50:45 +02:00
const envelope = mail . message . getEnvelope ( ) ;
const messageId = mail . message . messageId ( ) ;
const recipients = [ ] . concat ( envelope . to || [ ] ) ;
2020-02-29 23:01:03 +01:00
if ( recipients . length > 3 ) {
recipients . push ( '...and ' + recipients . splice ( 2 ) . length + ' more' ) ;
}
2026-09-15 13:00:32 +02:00
this . logger . info ( {
tnx : 'send' ,
2020-02-29 23:01:03 +01:00
messageId ,
2026-09-15 13:00:32 +02:00
cid : this . id
} , 'Sending message %s using #%s to <%s>' , messageId , this . id , recipients . join ( ', ' ) ) ;
2020-02-29 23:01:03 +01:00
if ( mail . data . dsn ) {
envelope . dsn = mail . data . dsn ;
}
2025-12-25 10:58:28 +01:00
// RFC 8689: Pass requireTLSExtensionEnabled to envelope for MAIL FROM parameter
if ( mail . data . requireTLSExtensionEnabled ) {
envelope . requireTLSExtensionEnabled = mail . data . requireTLSExtensionEnabled ;
}
2020-02-29 23:01:03 +01:00
this . connection . send ( envelope , mail . message . createReadStream ( ) , ( err , info ) => {
this . messages ++ ;
if ( err ) {
this . connection . close ( ) ;
this . emit ( 'error' , err ) ;
return callback ( err ) ;
}
info . envelope = {
from : envelope . from ,
to : envelope . to
} ;
info . messageId = messageId ;
setImmediate ( ( ) => {
if ( this . messages >= this . options . maxMessages ) {
2026-04-28 12:50:45 +02:00
const err = new Error ( 'Resource exhausted' ) ;
2026-02-05 08:49:11 +01:00
err . code = errors . EMAXLIMIT ;
2020-02-29 23:01:03 +01:00
this . connection . close ( ) ;
this . emit ( 'error' , err ) ;
2026-09-15 13:00:32 +02:00
}
else {
2020-02-29 23:01:03 +01:00
this . pool . _checkRateLimit ( ( ) => {
this . available = true ;
this . emit ( 'available' ) ;
} ) ;
}
} ) ;
callback ( null , info ) ;
} ) ;
}
/**
* Closes the connection
*/
close ( ) {
this . _connected = false ;
if ( this . auth && this . auth . oauth2 ) {
this . auth . oauth2 . removeAllListeners ( ) ;
}
if ( this . connection ) {
this . connection . close ( ) ;
}
this . emit ( 'close' ) ;
}
}
2026-09-15 13:00:32 +02:00
exports . default = PoolResource ;
module . exports = exports . default ;
Object . defineProperty ( module . exports , 'default' , { value : exports . default , enumerable : false , writable : true , configurable : true } ) ;