source: node_modules/nodemailer/lib/smtp-pool/pool-resource.js

main
Last change on this file was 81bc7da, checked in by Klimentina Efremova <klimentina08642@…>, 3 months ago

Initial commit

  • Property mode set to 100644
File size: 8.1 KB
Line 
1'use strict';
2
3const SMTPConnection = require('../smtp-connection');
4const assign = require('../shared').assign;
5const XOAuth2 = require('../xoauth2');
6const EventEmitter = require('events');
7
8/**
9 * Creates an element for the pool
10 *
11 * @constructor
12 * @param {Object} options SMTPPool instance
13 */
14class PoolResource extends EventEmitter {
15 constructor(pool) {
16 super();
17
18 this.pool = pool;
19 this.options = pool.options;
20 this.logger = this.pool.logger;
21
22 if (this.options.auth) {
23 switch ((this.options.auth.type || '').toString().toUpperCase()) {
24 case 'OAUTH2': {
25 let oauth2 = new XOAuth2(this.options.auth, this.logger);
26 oauth2.provisionCallback = (this.pool.mailer && this.pool.mailer.get('oauth2_provision_cb')) || oauth2.provisionCallback;
27 this.auth = {
28 type: 'OAUTH2',
29 user: this.options.auth.user,
30 oauth2,
31 method: 'XOAUTH2'
32 };
33 oauth2.on('token', token => this.pool.mailer.emit('token', token));
34 oauth2.on('error', err => this.emit('error', err));
35 break;
36 }
37 default:
38 if (!this.options.auth.user && !this.options.auth.pass) {
39 break;
40 }
41 this.auth = {
42 type: (this.options.auth.type || '').toString().toUpperCase() || 'LOGIN',
43 user: this.options.auth.user,
44 credentials: {
45 user: this.options.auth.user || '',
46 pass: this.options.auth.pass,
47 options: this.options.auth.options
48 },
49 method: (this.options.auth.method || '').trim().toUpperCase() || this.options.authMethod || false
50 };
51 }
52 }
53
54 this._connection = false;
55 this._connected = false;
56
57 this.messages = 0;
58 this.available = true;
59 }
60
61 /**
62 * Initiates a connection to the SMTP server
63 *
64 * @param {Function} callback Callback function to run once the connection is established or failed
65 */
66 connect(callback) {
67 this.pool.getSocket(this.options, (err, socketOptions) => {
68 if (err) {
69 return callback(err);
70 }
71
72 let returned = false;
73 let options = this.options;
74 if (socketOptions && socketOptions.connection) {
75 this.logger.info(
76 {
77 tnx: 'proxy',
78 remoteAddress: socketOptions.connection.remoteAddress,
79 remotePort: socketOptions.connection.remotePort,
80 destHost: options.host || '',
81 destPort: options.port || '',
82 action: 'connected'
83 },
84 'Using proxied socket from %s:%s to %s:%s',
85 socketOptions.connection.remoteAddress,
86 socketOptions.connection.remotePort,
87 options.host || '',
88 options.port || ''
89 );
90
91 options = assign(false, options);
92 Object.keys(socketOptions).forEach(key => {
93 options[key] = socketOptions[key];
94 });
95 }
96
97 this.connection = new SMTPConnection(options);
98
99 this.connection.once('error', err => {
100 this.emit('error', err);
101 if (returned) {
102 return;
103 }
104 returned = true;
105 return callback(err);
106 });
107
108 this.connection.once('end', () => {
109 this.close();
110 if (returned) {
111 return;
112 }
113 returned = true;
114
115 let timer = setTimeout(() => {
116 if (returned) {
117 return;
118 }
119 // still have not returned, this means we have an unexpected connection close
120 let err = new Error('Unexpected socket close');
121 if (this.connection && this.connection._socket && this.connection._socket.upgrading) {
122 // starttls connection errors
123 err.code = 'ETLS';
124 }
125 callback(err);
126 }, 1000);
127
128 try {
129 timer.unref();
130 } catch (E) {
131 // Ignore. Happens on envs with non-node timer implementation
132 }
133 });
134
135 this.connection.connect(() => {
136 if (returned) {
137 return;
138 }
139
140 if (this.auth && (this.connection.allowsAuth || options.forceAuth)) {
141 this.connection.login(this.auth, err => {
142 if (returned) {
143 return;
144 }
145 returned = true;
146
147 if (err) {
148 this.connection.close();
149 this.emit('error', err);
150 return callback(err);
151 }
152
153 this._connected = true;
154 callback(null, true);
155 });
156 } else {
157 returned = true;
158 this._connected = true;
159 return callback(null, true);
160 }
161 });
162 });
163 }
164
165 /**
166 * Sends an e-mail to be sent using the selected settings
167 *
168 * @param {Object} mail Mail object
169 * @param {Function} callback Callback function
170 */
171 send(mail, callback) {
172 if (!this._connected) {
173 return this.connect(err => {
174 if (err) {
175 return callback(err);
176 }
177 return this.send(mail, callback);
178 });
179 }
180
181 let envelope = mail.message.getEnvelope();
182 let messageId = mail.message.messageId();
183
184 let recipients = [].concat(envelope.to || []);
185 if (recipients.length > 3) {
186 recipients.push('...and ' + recipients.splice(2).length + ' more');
187 }
188 this.logger.info(
189 {
190 tnx: 'send',
191 messageId,
192 cid: this.id
193 },
194 'Sending message %s using #%s to <%s>',
195 messageId,
196 this.id,
197 recipients.join(', ')
198 );
199
200 if (mail.data.dsn) {
201 envelope.dsn = mail.data.dsn;
202 }
203
204 this.connection.send(envelope, mail.message.createReadStream(), (err, info) => {
205 this.messages++;
206
207 if (err) {
208 this.connection.close();
209 this.emit('error', err);
210 return callback(err);
211 }
212
213 info.envelope = {
214 from: envelope.from,
215 to: envelope.to
216 };
217 info.messageId = messageId;
218
219 setImmediate(() => {
220 let err;
221 if (this.messages >= this.options.maxMessages) {
222 err = new Error('Resource exhausted');
223 err.code = 'EMAXLIMIT';
224 this.connection.close();
225 this.emit('error', err);
226 } else {
227 this.pool._checkRateLimit(() => {
228 this.available = true;
229 this.emit('available');
230 });
231 }
232 });
233
234 callback(null, info);
235 });
236 }
237
238 /**
239 * Closes the connection
240 */
241 close() {
242 this._connected = false;
243 if (this.auth && this.auth.oauth2) {
244 this.auth.oauth2.removeAllListeners();
245 }
246 if (this.connection) {
247 this.connection.close();
248 }
249 this.emit('close');
250 }
251}
252
253module.exports = PoolResource;
Note: See TracBrowser for help on using the repository browser.