Bump email

and include poplib to avoid example warning.
This commit is contained in:
Dave Conway-Jones 2022-01-01 19:11:01 +00:00
parent 4ee6e57111
commit e6930274e2
No known key found for this signature in database
GPG Key ID: 88BA2B8A411BE9FF
3 changed files with 870 additions and 5 deletions

View File

@ -14,7 +14,7 @@ module.exports = function(RED) {
"use strict";
var util = require("util");
var Imap = require('imap');
var POP3Client = require("poplib");
var POP3Client = require("./poplib.js");
var nodemailer = require("nodemailer");
var simpleParser = require("mailparser").simpleParser;
var SMTPServer = require("smtp-server").SMTPServer;

View File

@ -1,17 +1,15 @@
{
"name": "node-red-node-email",
"version": "1.13.0",
"version": "1.14.0",
"description": "Node-RED nodes to send and receive simple emails.",
"dependencies": {
"imap": "^0.8.19",
"poplib": "^0.1.7",
"mailparser": "^3.4.0",
"nodemailer": "^6.7.0",
"nodemailer": "^6.7.2",
"smtp-server": "^3.9.0"
},
"bundledDependencies": [
"imap",
"poplib",
"mailparser",
"nodemailer",
"smtp-server"

867
social/email/poplib.js Normal file
View File

@ -0,0 +1,867 @@
/*
Node.js POP3 client library
Copyright (C) 2011-2013 by Ditesh Shashikant Gathani <ditesh@gathani.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
var net = require("net"),
tls = require("tls"),
util = require("util"),
crypto = require("crypto"),
events = require("events");
// Constructor
function POP3Client(port, host, options) {
if (options === undefined) options = {};
// Optional constructor arguments
var enabletls = options.enabletls !== undefined ? options.enabletls: false;
var ignoretlserrs = options.ignoretlserrs !== undefined ? options.ignoretlserrs: false;
var debug = options.debug || false;
var tlsDirectOpts = options.tlsopts !== undefined ? options.tlsopts: {};
// Private variables follow
var self = this;
var response = null;
var checkResp = true;
var bufferedData = "";
var state = 0;
var locked = false;
var multiline = false;
var socket = null;
var tlssock = null;
var callback = function(resp, data) {
if (resp === false) {
locked = false;
callback = function() {};
self.emit("connect", false, data);
} else {
// Checking for APOP support
var banner = data.trim();
var bannerComponents = banner.split(" ");
for(var i=0; i < bannerComponents.length; i++) {
if (bannerComponents[i].indexOf("@") > 0) {
self.data["apop"] = true;
self.data["apop-timestamp"] = bannerComponents[i];
break;
}
}
state = 1;
self.data["banner"] = banner;
self.emit("connect", true, data);
}
};
// Public variables follow
this.data = {
host: host,
port: port,
banner: "",
stls: false,
apop: false,
username: "",
tls: enabletls,
ignoretlserrs: ignoretlserrs
};
// Privileged methods follow
this.setCallback = function(cb) { callback = cb; };
this.getCallback = function() { return callback };
this.setState = function(val) { state = val; };
this.getState = function() { return state; };
this.setLocked = function(val) { locked = val; };
this.getLocked = function() { return locked; };
this.setMultiline = function(val) { multiline = val; };
this.getMultiline = function() { return multiline; };
// Writes to remote server socket
this.write = function(command, argument) {
var text = command;
if (argument !== undefined) text = text + " " + argument + "\r\n";
else text = text + "\r\n";
if (debug) console.log("Client: " + util.inspect(text));
socket.write(text);
};
// Kills the socket connection
this.end = function() {
socket.end();
};
// Upgrades a standard unencrypted TCP connection to use TLS
// Liberally copied and modified from https://gist.github.com/848444
// starttls() should be a private function, but I can't figure out
// how to get a public prototypal method (stls) to talk to private method (starttls)
// which references private variables without going through a privileged method
this.starttls = function(options) {
var s = socket;
s.removeAllListeners("end");
s.removeAllListeners("data");
s.removeAllListeners("error");
socket=null;
var sslcontext = require('crypto').createCredentials(options);
var pair = tls.createSecurePair(sslcontext, false);
var cleartext = pipe(pair);
pair.on('secure', function() {
var verifyError = pair.ssl.verifyError();
cleartext.authorized = true;
if (verifyError) {
cleartext.authorized = false;
cleartext.authorizationError = verifyError;
}
cleartext.on("data", onData);
cleartext.on("error", onError);
cleartext.on("end", onEnd);
socket=cleartext;
(self.getCallback())(cleartext.authorized, cleartext.authorizationError);
});
cleartext._controlReleased = true;
function pipe(pair) {
pair.encrypted.pipe(s);
s.pipe(pair.encrypted);
pair.fd = s.fd;
var cleartext = pair.cleartext;
cleartext.socket = s;
cleartext.encrypted = pair.encrypted;
cleartext.authorized = false;
function onerror(e) {
if (cleartext._controlReleased) cleartext.emit('error', e);
}
function onclose() {
s.removeListener('error', onerror);
s.removeListener('close', onclose);
}
s.on('error', onerror);
s.on('close', onclose);
return cleartext;
}
};
// Private methods follow
// Event handlers follow
function onData(data) {
data = data.toString("ascii");
bufferedData += data;
if (debug) console.log("Server: " + util.inspect(data));
if (checkResp === true) {
if (bufferedData.substr(0, 3) === "+OK") {
checkResp = false;
response = true;
} else if (bufferedData.substr(0, 4) === "-ERR") {
checkResp = false;
response = false;
// The following is only used for SASL
} else if (multiline === false) {
checkResp = false;
response = true;
}
}
if (checkResp === false) {
if (multiline === true && (response === false || bufferedData.substr(bufferedData.length-5) === "\r\n.\r\n")) {
// Make a copy to avoid race conditions
var responseCopy = response;
var bufferedDataCopy = bufferedData;
response = null;
checkResp = true;
multiline = false;
bufferedData = "";
callback(responseCopy, bufferedDataCopy);
} else if (multiline === false) {
// Make a copy to avoid race conditions
var responseCopy = response;
var bufferedDataCopy = bufferedData;
response = null;
checkResp = true;
multiline = false;
bufferedData = "";
callback(responseCopy, bufferedDataCopy);
}
}
}
function onError(err) {
if (err.errno === 111) self.emit("connect", false, err);
else self.emit("error", err);
}
function onEnd(data) {
self.setState(0);
socket = null;
}
function onClose() {
self.emit("close");
}
// Constructor code follows
// Set up EventEmitter constructor function
events.EventEmitter.call(this);
// Remote end socket
if (enabletls === true) {
tlssock = tls.connect({
host: host,
port: port,
rejectUnauthorized: !self.data.ignoretlserrs
}, function() {
if (tlssock.authorized === false &&
self.data["ignoretlserrs"] === false)
self.emit("tls-error", tlssock.authorizationError);
}
);
socket = tlssock;
} else socket = new net.createConnection(port, host);
// Set up event handlers
socket.on("data", onData);
socket.on("error", onError);
socket.on("end", onEnd);
socket.on("close", onClose);
}
util.inherits(POP3Client, events.EventEmitter);
POP3Client.prototype.login = function (username, password) {
var self = this;
if (self.getState() !== 1) self.emit("invalid-state", "login");
else if (self.getLocked() === true) self.emit("locked", "login");
else {
self.setLocked(true);
self.setCallback(function(resp, data) {
if (resp === false) {
self.setLocked(false);
self.setCallback(function() {});
self.emit("login", false, data);
} else {
self.setCallback(function(resp, data) {
self.setLocked(false);
self.setCallback(function() {});
if (resp !== false) self.setState(2);
self.emit("login", resp, data);
});
self.setMultiline(false);
self.write("PASS", password);
}
});
self.setMultiline(false);
self.write("USER", username);
}
};
// SASL AUTH implementation
// Currently supports SASL PLAIN and CRAM-MD5
POP3Client.prototype.auth = function (type, username, password) {
type = type.toUpperCase();
var self = this;
var types = {"PLAIN": 1, "CRAM-MD5": 1};
var initialresp = "";
if (self.getState() !== 1) self.emit("invalid-state", "auth");
else if (self.getLocked() === true) self.emit("locked", "auth");
if ((type in types) === false) {
self.emit("auth", false, "Invalid auth type", null);
return;
}
function tlsok() {
if (type === "PLAIN") {
initialresp = " " + new Buffer(username + "\u0000" + username + "\u0000" + password).toString("base64") + "=";
self.setCallback(function(resp, data) {
if (resp !== false) self.setState(2);
self.emit("auth", resp, data, data);
});
} else if (type === "CRAM-MD5") {
self.setCallback(function(resp, data) {
if (resp === false) self.emit("auth", resp, "Server responded -ERR to AUTH CRAM-MD5", data);
else {
var challenge = new Buffer(data.trim().substr(2), "base64").toString();
var hmac = crypto.createHmac("md5", password);
var response = new Buffer(username + " " + hmac.update(challenge).digest("hex")).toString("base64");
self.setCallback(function(resp, data) {
var errmsg = null;
if (resp !== false) self.setState(2);
else errmsg = "Server responded -ERR to response";
self.emit("auth", resp, null, data);
});
self.write(response);
}
});
}
self.write("AUTH " + type + initialresp);
}
if (self.data["tls"] === false && self.data["stls"] === false) {
// Remove all existing STLS listeners
self.removeAllListeners("stls");
self.on("stls", function(resp, rawdata) {
if (resp === false) {
// We (optionally) ignore self signed cert errors,
// in blatant violation of RFC 2595, Section 2.4
if (self.data["ignoretlserrs"] === true && rawdata === "DEPTH_ZERO_SELF_SIGNED_CERT") tlsok();
else self.emit("auth", false, "Unable to upgrade connection to STLS", rawdata);
} else tlsok();
});
self.stls();
} else tlsok();
};
POP3Client.prototype.apop = function (username, password) {
var self = this;
if (self.getState() !== 1) self.emit("invalid-state", "apop");
else if (self.getLocked() === true) self.emit("locked", "apop");
else if (self.data["apop"] === false) self.emit("apop", false, "APOP support not detected on remote server");
else {
self.setLocked(true);
self.setCallback(function(resp, data) {
self.setLocked(false);
self.setCallback(function() {});
if (resp === true) self.setState(2);
self.emit("apop", resp, data);
});
self.setMultiline(false);
self.write("APOP", username + " " + crypto.createHash("md5").update(self.data["apop-timestamp"] + password).digest("hex"));
}
};
POP3Client.prototype.stls = function() {
var self = this;
if (self.getState() !== 1) self.emit("invalid-state", "stls");
else if (self.getLocked() === true) self.emit("locked", "stls");
else if (self.data["tls"] === true) self.emit("stls", false, "Unable to execute STLS as TLS connection already established");
else {
self.setLocked(true);
self.setCallback(function(resp, data) {
self.setLocked(false);
self.setCallback(function() {});
if (resp === true) {
self.setCallback(function(resp, data) {
if (resp === false && self.data["ignoretlserrs"] === true && data === "DEPTH_ZERO_SELF_SIGNED_CERT")
resp = true;
self.data["stls"] = true;
self.emit("stls", resp, data);
});
self.starttls();
} else self.emit("stls", false, data);
});
self.setMultiline(false);
self.write("STLS");
}
};
POP3Client.prototype.top = function(msgnumber, lines) {
var self = this;
if (self.getState() !== 2) self.emit("invalid-state", "top");
else if (self.getLocked() === true) self.emit("locked", "top");
else {
self.setCallback(function(resp, data) {
var returnValue = null;
self.setLocked(false);
self.setCallback(function() {});
if (resp !== false) {
returnValue = "";
var startOffset = data.indexOf("\r\n", 0) + 2;
var endOffset = data.indexOf("\r\n.\r\n", 0) + 2;
if (endOffset > startOffset)
returnValue = data.substr(startOffset, endOffset-startOffset);
}
self.emit("top", resp, msgnumber, returnValue, data);
});
self.setMultiline(true);
self.write("TOP", msgnumber + " " + lines);
}
};
POP3Client.prototype.list = function(msgnumber) {
var self = this;
if (self.getState() !== 2) self.emit("invalid-state", "list");
else if (self.getLocked() === true) self.emit("locked", "list");
else {
self.setLocked(true);
self.setCallback(function(resp, data) {
var returnValue = null;
var msgcount = 0;
self.setLocked(false);
self.setCallback(function() {});
if (resp !== false) {
returnValue = [];
if (msgnumber !== undefined) {
msgcount = 1
listitem = data.split(" ");
returnValue[listitem[1]] = listitem[2];
} else {
var offset = 0;
var listitem = "";
var newoffset = 0;
var returnValue = [];
var startOffset = data.indexOf("\r\n", 0) + 2;
var endOffset = data.indexOf("\r\n.\r\n", 0) + 2;
if (endOffset > startOffset) {
data = data.substr(startOffset, endOffset-startOffset);
while(true) {
if (offset > endOffset)
break;
newoffset = data.indexOf("\r\n", offset);
if (newoffset < 0)
break;
msgcount++;
listitem = data.substr(offset, newoffset-offset);
listitem = listitem.split(" ");
returnValue[listitem[0]] = listitem[1];
offset = newoffset + 2;
}
}
}
}
self.emit("list", resp, msgcount, msgnumber, returnValue, data);
});
if (msgnumber !== undefined) self.setMultiline(false);
else self.setMultiline(true);
self.write("LIST", msgnumber);
}
};
POP3Client.prototype.stat = function() {
var self = this;
if (self.getState() !== 2) self.emit("invalid-state", "stat");
else if (self.getLocked() === true) self.emit("locked", "stat");
else {
self.setLocked(true);
self.setCallback(function(resp, data) {
var returnValue = null;
self.setLocked(false);
self.setCallback(function() {});
if (resp !== false) {
listitem = data.split(" ");
returnValue = {
"count": listitem[1].trim(),
"octets": listitem[2].trim(),
};
}
self.emit("stat", resp, returnValue, data);
});
self.setMultiline(false);
self.write("STAT", undefined);
}
};
POP3Client.prototype.uidl = function(msgnumber) {
var self = this;
if (self.getState() !== 2) self.emit("invalid-state", "uidl");
else if (self.getLocked() === true) self.emit("locked", "uidl");
else {
self.setLocked(true);
self.setCallback(function(resp, data) {
var returnValue = null;
self.setLocked(false);
self.setCallback(function() {});
if (resp !== false) {
returnValue = [];
if (msgnumber !== undefined) {
listitem = data.split(" ");
returnValue[listitem[1]] = listitem[2].trim();
} else {
var offset = 0;
var listitem = "";
var newoffset = 0;
var returnValue = [];
var startOffset = data.indexOf("\r\n", 0) + 2;
var endOffset = data.indexOf("\r\n.\r\n", 0) + 2;
if (endOffset > startOffset) {
data = data.substr(startOffset, endOffset-startOffset);
endOffset -= startOffset;
while (offset < endOffset) {
newoffset = data.indexOf("\r\n", offset);
listitem = data.substr(offset, newoffset-offset);
listitem = listitem.split(" ");
returnValue[listitem[0]] = listitem[1];
offset = newoffset + 2;
}
}
}
}
self.emit("uidl", resp, msgnumber, returnValue, data);
});
if (msgnumber !== undefined) self.setMultiline(false);
else self.setMultiline(true);
self.write("UIDL", msgnumber);
}
};
POP3Client.prototype.retr = function(msgnumber) {
var self = this;
if (self.getState() !== 2) self.emit("invalid-state", "retr");
else if (self.getLocked() === true) self.emit("locked", "retr");
else {
self.setLocked(true);
self.setCallback(function(resp, data) {
var returnValue = null;
self.setLocked(false);
self.setCallback(function() {});
if (resp !== false) {
var startOffset = data.indexOf("\r\n", 0) + 2;
var endOffset = data.indexOf("\r\n.\r\n", 0);
returnValue = data.substr(startOffset, endOffset-startOffset);
}
self.emit("retr", resp, msgnumber, returnValue, data);
});
self.setMultiline(true);
self.write("RETR", msgnumber);
}
};
POP3Client.prototype.dele = function(msgnumber) {
var self = this;
if (self.getState() !== 2) self.emit("invalid-state", "dele");
else if (self.getLocked() === true) self.emit("locked", "dele");
else {
self.setLocked(true);
self.setCallback(function(resp, data) {
self.setLocked(false);
self.setCallback(function() {});
self.emit("dele", resp, msgnumber, data);
});
self.setMultiline(false);
self.write("DELE", msgnumber);
}
};
POP3Client.prototype.noop = function() {
var self = this;
if (self.getState() !== 2) self.emit("invalid-state", "noop");
else if (self.getLocked() === true) self.emit("locked", "noop");
else {
self.setLocked(true);
self.setCallback(function(resp, data) {
self.setLocked(false);
self.setCallback(function() {});
self.emit("noop", resp, data);
});
self.setMultiline(false);
self.write("NOOP", undefined);
}
};
POP3Client.prototype.rset = function() {
var self = this;
if (self.getState() !== 2) self.emit("invalid-state", "rset");
else if (self.getLocked() === true) self.emit("locked", "rset");
else {
self.setLocked(true);
self.setCallback(function(resp, data) {
self.setLocked(false);
self.setCallback(function() {});
self.emit("rset", resp, data);
});
self.setMultiline(false);
self.write("RSET", undefined);
}
};
POP3Client.prototype.capa = function() {
var self = this;
if (self.getState() === 0) self.emit("invalid-state", "quit");
else if (self.getLocked() === true) self.emit("locked", "capa");
else {
self.setLocked(true);
self.setCallback(function(resp, data) {
var returnValue = null;
self.setLocked(false);
self.setCallback(function() {});
if (resp === true) {
var startOffset = data.indexOf("\r\n", 0) + 2;
var endOffset = data.indexOf("\r\n.\r\n", 0);
returnValue = data.substr(startOffset, endOffset-startOffset);
returnValue = returnValue.split("\r\n");
}
self.emit("capa", resp, returnValue, data);
});
self.setMultiline(true);
self.write("CAPA", undefined);
}
};
POP3Client.prototype.quit = function() {
var self = this;
if (self.getState() === 0) self.emit("invalid-state", "quit");
else if (self.getLocked() === true) self.emit("locked", "quit");
else {
self.setLocked(true);
self.setCallback(function(resp, data) {
self.setLocked(false);
self.setCallback(function() {});
self.end();
self.emit("quit", resp, data);
});
self.setMultiline(false);
self.write("QUIT", undefined);
}
};
module.exports = POP3Client;