sendEnvelope: function(conn) { var envelope = composer.getEnvelope(); slog.log('smtp:sendEnvelope', { _envelope: envelope }); conn.useEnvelope(envelope); },
return new Promise(function(resolve, reject) { var auth = { // Someday, `null` might be a valid value, so be careful here user: (credentials.outgoingUsername !== undefined ? credentials.outgoingUsername : credentials.username), pass: (credentials.outgoingPassword !== undefined ? credentials.outgoingPassword : credentials.password), xoauth2: credentials.oauth2 ? credentials.oauth2.accessToken : null }; slog.log('smtp:connect', { _auth: auth, usingOauth2: !!credentials.oauth2, connInfo: connInfo }); conn = new SmtpClient( connInfo.hostname, connInfo.port, { useSecureTransport: (connInfo.crypto === 'ssl' || connInfo.crypto === true), starttls: connInfo.crypto === 'starttls', auth: auth }); var connectTimeout = setTimeout(function() { conn.onerror('unresponsive-server'); conn.close(); }, syncbase.CONNECT_TIMEOUT_MS); function clearConnectTimeout() { if (connectTimeout) { clearTimeout(connectTimeout); connectTimeout = null; } } conn.onidle = function() { clearConnectTimeout(); slog.info('smtp:connected', connInfo); conn.onidle = conn.onclose = conn.onerror = function() { /* noop */ }; resolve(conn); }; conn.onerror = function(err) { clearConnectTimeout(); reject(err); }; // if the connection closes without any of the other callbacks, // the server isn't responding properly. conn.onclose = function() { clearConnectTimeout(); reject('server-maybe-offline'); }; conn.connect(); });
conn.onclose = function() { slog.log('smtp:onclose'); var idx = this._activeConnections.indexOf(conn); if (idx !== -1) { this._activeConnections.splice(idx, 1); } else { slog.error('smtp:dead-unknown-connection'); } }.bind(this);
conn.onready = function(badRecipients) { slog.log('smtp:onready'); if (badRecipients.length) { conn.close(); slog.warn('smtp:bad-recipients', { badRecipients: badRecipients }); callbacks.onError('bad-recipient', badRecipients); } else { sendingMessage = true; callbacks.sendMessage(conn); } };
conn.ondone = function(success) { conn.close(); if (success) { slog.log('smtp:sent'); callbacks.onSendComplete(conn); } else { slog.error('smtp:send-failed'); // We don't have an error to reference here, but we stored // the most recent SMTP error, which should tell us why the // server rejected the message. var err = client.analyzeSmtpError(conn, null, sendingMessage); callbacks.onError(err, /* badAddresses: */ null); } };
composer.withMessageBlob({ includeBcc: false }, function(blob) { slog.log('smtp:sending-blob', { size: blob.size }); // simplesmtp's SMTPClient does not understand Blobs, so we // issue the write directly. All that it cares about is // knowing whether our data payload included a trailing // \r\n. We had hoped to avoid this silliness in bug 885110, // but SMTPClient still does not support blobs yet, so we // still need this. conn.socket.send(blob); // SMTPClient tracks the last bytes it has written in _lastDataBytes // to this end and writes the \r\n if they aren't the last bytes // written. Since we know that mailcomposer always ends the buffer // with \r\n we just set that state directly ourselves. conn._lastDataBytes = '\r\n'; // this does not actually terminate the connection; just tells the // client to flush stuff, etc. conn.end(); });
ImapClient.prototype._processResponse = function(response) { processResponse.apply(this, arguments); var cmd = (response && response.command || '').toString() .toUpperCase().trim(); if (['NO', 'BAD'].indexOf(cmd) !== -1) { slog.log('imap:protocol-error', { humanReadable: response.humanReadable, responseCode: response.code, // Include the command structure commandData: this._currentCommand && this._currentCommand.request && imapHandler.compiler(this._currentCommand.request) }); this._lastImapError = { // To most accurately report STARTTLS issues, latch the active command // at the time of the failure rather than just the response. (An evil // attacker could say "NO SUCKER" instead of "NO STARTTLS" or // something.) command: this._currentCommand, response: response }; } }
function(conn, rawError, wasSending) { var err = rawError; // If the error object is just an exception with no useful data, // try looking at recent SMTP errors. if ((err && !err.statusCode && err.name === 'Error') || !err) { err = conn && conn._lastSmtpError || null; } if (!err) { err = 'null-error'; } var wasOauth = conn && !!conn.options.auth.xoauth2; var normalizedError = 'unknown'; // If we were able to extract a negative SMTP response, we can // analyze the statusCode: if (err.statusCode) { // Example SMTP error: // { "statusCode": 535, // "enhancedStatus": "5.7.8", // "data": "Wrong username or password, crook!", // "line": "535 5.7.8 Wrong username or password, crook!", // "success": false } switch (err.statusCode) { case 535: if (wasOauth) { normalizedError = 'needs-oauth-reauth'; } else { normalizedError = 'bad-user-or-pass'; } break; case 501: // Invalid Syntax if (wasSending) { normalizedError = 'bad-message'; } else { normalizedError = 'server-maybe-offline'; } break; case 550: // Mailbox Unavailable case 551: // User not local, will not send case 553: // Mailbox name not allowed case 554: // Transaction failed (in response to bad addresses) normalizedError = 'bad-address'; break; case 500: normalizedError = 'server-problem'; break; default: if (wasSending) { normalizedError = 'bad-message'; } else { normalizedError = 'unknown'; } break; } } // Socket errors only have a name: else if (err.name === 'ConnectionRefusedError') { normalizedError = 'unresponsive-server'; } else if (/^Security/.test(err.name)) { normalizedError = 'bad-security'; } // If we provided a string only, it's probably already normalized else if (typeof err === 'string') { normalizedError = err; } slog.log('smtp:analyzed-error', { statusCode: err.statusCode, enhancedStatus: err.enhancedStatus, rawError: rawError, rawErrorName: rawError && rawError.name, rawErrorMessage: rawError && rawError.message, rawErrorStack: rawError && rawError.stack, normalizedError: normalizedError, errorName: err.name, errorMessage: err.message, wasSending: wasSending }); return normalizedError; };
log: function(ignoredTag, msg) { slog.log(slogTag, { msg: msg }); },
onSendComplete: function(conn) { slog.log('smtp:sent'); callback(null); },