11const { createConnection} = require ( 'net' ) ;
2+ const { connect} = require ( 'tls' ) ;
23const { resolveMx} = require ( 'dns' ) ;
34const { DKIMSign} = require ( 'dkim-signer' ) ;
45const CRLF = '\r\n' ;
@@ -21,8 +22,11 @@ module.exports = function (options) {
2122 const dkimKeySelector = ( options . dkim || { } ) . keySelector || 'dkim' ;
2223 const devPort = options . devPort || - 1 ;
2324 const devHost = options . devHost || 'localhost' ;
24- const smtpPort = options . smtpPort || 25
25- const smtpHost = options . smtpHost || - 1
25+ const smtpPort = options . smtpPort || 25 ;
26+ const smtpHost = options . smtpHost || - 1 ;
27+ const rejectUnauthorized = options . rejectUnauthorized ;
28+ const autoEHLO = options . autoEHLO ;
29+
2630 /*
2731 * 邮件服务返回代码含义 Mail service return code Meaning
2832 * 500 格式错误,命令不可识别(此错误也包括命令行过长)format error, command unrecognized (This error also includes command line too long)
@@ -64,7 +68,7 @@ module.exports = function (options) {
6468 host = getHost ( recipients [ i ] ) ;
6569 ( groups [ host ] || ( groups [ host ] = [ ] ) ) . push ( recipients [ i ] )
6670 }
67- return groups
71+ return groups ;
6872 }
6973
7074 /**
@@ -74,21 +78,22 @@ module.exports = function (options) {
7478 if ( devPort === - 1 ) { // not in development mode -> search the MX
7579 resolveMx ( domain , function ( err , data ) {
7680 if ( err ) {
77- return callback ( err )
81+ return callback ( err ) ;
7882 }
7983
8084 data . sort ( function ( a , b ) { return a . priority > b . priority } ) ;
8185 logger . debug ( 'mx resolved: ' , data ) ;
8286
8387 if ( ! data || data . length === 0 ) {
84- return callback ( new Error ( 'can not resolve Mx of <' + domain + '>' ) )
88+ return callback ( new Error ( 'can not resolve Mx of <' + domain + '>' ) ) ;
8589 }
86- if ( smtpHost !== - 1 ) data . push ( { exchange :smtpHost } )
90+ if ( smtpHost !== - 1 ) data . push ( { exchange :smtpHost } ) ;
91+
8792 function tryConnect ( i ) {
8893 if ( i >= data . length ) return callback ( new Error ( 'can not connect to any SMTP server' ) ) ;
8994
9095 const sock = createConnection ( smtpPort , data [ i ] . exchange ) ;
91-
96+
9297 sock . on ( 'error' , function ( err ) {
9398 logger . error ( 'Error on connectMx for: ' , data [ i ] , err ) ;
9499 tryConnect ( ++ i )
@@ -97,7 +102,7 @@ module.exports = function (options) {
97102 sock . on ( 'connect' , function ( ) {
98103 logger . debug ( 'MX connection created: ' , data [ i ] . exchange ) ;
99104 sock . removeAllListeners ( 'error' ) ;
100- callback ( null , sock )
105+ callback ( null , sock ) ;
101106 } )
102107 }
103108
@@ -113,7 +118,7 @@ module.exports = function (options) {
113118 sock . on ( 'connect' , function ( ) {
114119 logger . debug ( 'MX (development) connection created: ' + devHost + ':' + devPort ) ;
115120 sock . removeAllListeners ( 'error' ) ;
116- callback ( null , sock )
121+ callback ( null , sock ) ;
117122 } )
118123 }
119124 }
@@ -123,12 +128,12 @@ module.exports = function (options) {
123128 connectMx ( domain , function ( err , sock ) {
124129 if ( err ) {
125130 logger . error ( 'error on connectMx' , err . stack ) ;
126- return callback ( err )
131+ return callback ( err ) ;
127132 }
128133
129134 function w ( s ) {
130135 logger . debug ( 'send ' + domain + '>' + s ) ;
131- sock . write ( s + CRLF )
136+ sock . write ( s + CRLF ) ;
132137 }
133138
134139 sock . setEncoding ( 'utf8' ) ;
@@ -138,14 +143,14 @@ module.exports = function (options) {
138143 parts = data . split ( CRLF ) ;
139144 const parts_length = parts . length - 1 ;
140145 for ( let i = 0 , len = parts_length ; i < len ; i ++ ) {
141- onLine ( parts [ i ] )
146+ onLine ( parts [ i ] ) ;
142147 }
143- data = parts [ parts . length - 1 ]
148+ data = parts [ parts . length - 1 ] ;
144149 } ) ;
145150
146151 sock . on ( 'error' , function ( err ) {
147- logger . error ( 'fail to connect ' + domain )
148- callback ( err )
152+ logger . error ( 'fail to connect ' + domain ) ;
153+ callback ( err ) ;
149154 } ) ;
150155
151156 let data = '' ;
@@ -155,6 +160,7 @@ module.exports = function (options) {
155160 const login = [ ] ;
156161 let parts ;
157162 let cmd ;
163+ let upgraded = false ;
158164
159165 /*
160166 if(mail.user && mail.pass){
@@ -167,7 +173,7 @@ module.exports = function (options) {
167173 queue . push ( 'MAIL FROM:<' + from + '>' ) ;
168174 const recipients_length = recipients . length ;
169175 for ( let i = 0 ; i < recipients_length ; i ++ ) {
170- queue . push ( 'RCPT TO:<' + recipients [ i ] + '>' )
176+ queue . push ( 'RCPT TO:<' + recipients [ i ] + '>' ) ;
171177 }
172178 queue . push ( 'DATA' ) ;
173179 queue . push ( 'QUIT' ) ;
@@ -178,22 +184,80 @@ module.exports = function (options) {
178184 case 220 :
179185 //* 220 on server ready
180186 //* 220 服务就绪
181- if ( / \b e s m t p \b / i. test ( msg ) ) {
182- // TODO: determin AUTH type; auth login, auth crm-md5, auth plain
183- cmd = 'EHLO'
184- } else {
185- cmd = 'HELO'
186- }
187- w ( cmd + ' ' + srcHost ) ;
188- break ;
187+ if ( upgraded === "in-progress" ) {
188+ sock . removeAllListeners ( 'data' ) ;
189+
190+ let original = sock ;
191+ original . pause ( ) ;
192+
193+ let opts = {
194+ socket : sock ,
195+ host : sock . _host ,
196+ rejectUnauthorized,
197+ } ;
198+
199+ sock = connect (
200+ opts ,
201+ ( ) => {
202+ sock . on ( 'data' , function ( chunk ) {
203+ data += chunk ;
204+ parts = data . split ( CRLF ) ;
205+ const parts_length = parts . length - 1 ;
206+ for ( let i = 0 , len = parts_length ; i < len ; i ++ ) {
207+ onLine ( parts [ i ] )
208+ }
209+ data = parts [ parts . length - 1 ]
210+ } ) ;
211+
212+ sock . removeAllListeners ( 'close' ) ;
213+ sock . removeAllListeners ( 'end' ) ;
214+
215+ return ;
216+ }
217+ ) ;
218+
219+ sock . on ( 'error' , function ( err ) {
220+ logger . error ( 'Error on connectMx for: ' , err ) ;
221+ } ) ;
222+
223+ original . resume ( ) ;
224+ upgraded = true ;
225+ w ( "EHLO " + srcHost ) ;
226+ break ;
227+ } else
228+ {
229+ if ( / \b e s m t p \b / i. test ( msg ) || autoEHLO ) {
230+ // TODO: determin AUTH type; auth login, auth crm-md5, auth plain
231+ cmd = 'EHLO' ;
232+ } else {
233+ upgraded = true ;
234+ cmd = 'HELO' ;
235+ }
236+ w ( cmd + ' ' + srcHost ) ;
237+ break ;
238+ }
189239
190240 case 221 : // bye
241+ sock . end ( ) ;
242+ callback ( null , msg ) ;
243+ break ;
191244 case 235 : // verify ok
192245 case 250 : // operation OK
246+ if ( upgraded != true ) {
247+ if ( / \b S T A R T T L S \b / i. test ( msg ) ) {
248+ w ( 'STARTTLS' ) ;
249+ upgraded = "in-progress" ;
250+ } else {
251+ upgraded = true ;
252+ }
253+
254+ break ;
255+ }
256+
193257 case 251 : // foward
194258 if ( step === queue . length - 1 ) {
195259 logger . info ( 'OK:' , code , msg ) ;
196- callback ( null , msg )
260+ callback ( null , msg ) ;
197261 }
198262 w ( queue [ step ] ) ;
199263 step ++ ;
@@ -252,7 +316,7 @@ module.exports = function (options) {
252316 for ( let i = 0 ; i < addresses_length ; i ++ ) {
253317 results . push ( getAddress ( addresses [ i ] ) ) ;
254318 }
255- return results
319+ return results ;
256320 }
257321
258322 /**
@@ -289,15 +353,15 @@ module.exports = function (options) {
289353 let groups ;
290354 let srcHost ;
291355 if ( mail . to ) {
292- recipients = recipients . concat ( getAddresses ( mail . to ) )
356+ recipients = recipients . concat ( getAddresses ( mail . to ) ) ;
293357 }
294358
295359 if ( mail . cc ) {
296- recipients = recipients . concat ( getAddresses ( mail . cc ) )
360+ recipients = recipients . concat ( getAddresses ( mail . cc ) ) ;
297361 }
298362
299363 if ( mail . bcc ) {
300- recipients = recipients . concat ( getAddresses ( mail . bcc ) )
364+ recipients = recipients . concat ( getAddresses ( mail . bcc ) ) ;
301365 }
302366
303367 groups = groupRecipients ( recipients ) ;
@@ -307,7 +371,7 @@ module.exports = function (options) {
307371
308372 mailMe . build ( function ( err , message ) {
309373 if ( err ) {
310- logger . error ( 'Error on creating message : ' , err )
374+ logger . error ( 'Error on creating message : ' , err ) ;
311375 callback ( err , null ) ;
312376 return
313377 }
@@ -317,12 +381,12 @@ module.exports = function (options) {
317381 keySelector : dkimKeySelector ,
318382 domainName : srcHost
319383 } ) ;
320- message = signature + '\r\n' + message
384+ message = signature + '\r\n' + message ;
321385 }
322386 for ( let domain in groups ) {
323- sendToSMTP ( domain , srcHost , from , groups [ domain ] , message , callback )
387+ sendToSMTP ( domain , srcHost , from , groups [ domain ] , message , callback ) ;
324388 }
325389 } ) ;
326390 }
327- return sendmail
391+ return sendmail ;
328392} ;
0 commit comments