From 4b99a5f4d13041510fe0e8bcb98b33fa02a42456 Mon Sep 17 00:00:00 2001 From: andres <999999999e9@gmail.com> Date: Sat, 24 Oct 2020 11:07:16 +1000 Subject: [PATCH 01/86] Added support for V3 Added support for version 3. All the existing issues with v1 and v2c are also with v3 e.g. https://github.com/node-red/node-red-nodes/issues/679 snmp walker will not return more than 1000 objects, all following objects cannot be accessed Incorrect encryption (privacy) passphrase gives a timeout and not an error (protocol limitation?) Incorrect community string gives a timeout and not an error (protocol limitation?) Changes to the node are made with following details: Node-red v1.2.2 Node.js v12.6.0 Windows_NT 10.0.19841 x64 LE --- io/snmp/README.md | 82 +++++++++-- io/snmp/package.json | 7 +- io/snmp/snmp.html | 328 +++++++++++++++++++++++++++++++++++++------ io/snmp/snmp.js | 207 ++++++++++++++++++++++++--- 4 files changed, 545 insertions(+), 79 deletions(-) diff --git a/io/snmp/README.md b/io/snmp/README.md index 5067b11b..8caa1b28 100644 --- a/io/snmp/README.md +++ b/io/snmp/README.md @@ -18,9 +18,15 @@ Usage SNMP oids fetcher. Can fetch a single or comma separated list of oids. Triggered by any input. -`msg.host` may contain the host. +`msg.host` may contain the host including the port. -`msg.community` may contain the community. +`msg.community` may contain the community. (v1 and v2c only) + +`msg.username` may contain the username. (v3 only) + +`msg.authkey` may contain the digest security key. (v3 only) + +`msg.privkey` may contain the encryption security key. (v3 only) `msg.oid` may contain a comma separated list of oids to search for. (no spaces) @@ -28,6 +34,12 @@ The host configured in the edit config will override `msg.host`. Leave blank if The community configured in the edit config will override `msg.community`. Leave blank if you want to use `msg.community` to provide input. +The username configured in the edit config will override `msg.username`. Leave blank if you want to use `msg.username` to provide input. + +The digest security key configured in the edit config will override `msg.authkey`. Leave blank if you want to use `msg.authkey` to provide input. + +The encryption security key configured in the edit config will override `msg.privkey`. Leave blank if you want to use `msg.privkey` to provide input. + The oids configured in the edit config will override `msg.oid`. Leave blank if you want to use `msg.oid` to provide input. @@ -38,9 +50,15 @@ Values depends on the oids being requested. SNMP sets the value of one or more OIDs. -`msg.host` may contain the host. +`msg.host` may contain the host including the port. -`msg.community` may contain the community. +`msg.community` may contain the community. (v1 and v2c only) + +`msg.username` may contain the username. (v3 only) + +`msg.authkey` may contain the digest security key. (v3 only) + +`msg.privkey` may contain the encryption security key. (v3 only) `msg.varbinds` may contain an array of varbind JSON objects e.g.: ``` @@ -81,6 +99,12 @@ The host configured in the edit config will override `msg.host`. Leave blank if The community configured in the edit config will override `msg.community`. Leave blank if you want to use `msg.community` to provide input. +The username configured in the edit config will override `msg.username`. Leave blank if you want to use `msg.username` to provide input. + +The digest security key configured in the edit config will override `msg.authkey`. Leave blank if you want to use `msg.authkey` to provide input. + +The encryption security key configured in the edit config will override `msg.privkey`. Leave blank if you want to use `msg.privkey` to provide input. + The varbinds configured in the edit config will override `msg.varbinds`. Leave blank if you want to use `msg.varbinds` to provide input. @@ -89,16 +113,28 @@ The varbinds configured in the edit config will override `msg.varbinds`. Leave b Simple SNMP table oid fetcher. Triggered by any input. -`msg.host` may contain the host. +`msg.host` may contain the host including the port. -`msg.community` may contain the community. +`msg.community` may contain the community. (v1 and v2c only) -`msg.oid` may contain the oid of a single table to search for. +`msg.username` may contain the username. (v3 only) + +`msg.authkey` may contain the digest security key. (v3 only) + +`msg.privkey` may contain the encryption security key. (v3 only) + +`msg.oid` may contain a comma separated list of oids to search for. (no spaces) The host configured in the edit config will override `msg.host`. Leave blank if you want to use `msg.host` to provide input. The community configured in the edit config will override `msg.community`. Leave blank if you want to use `msg.community` to provide input. +The username configured in the edit config will override `msg.username`. Leave blank if you want to use `msg.username` to provide input. + +The digest security key configured in the edit config will override `msg.authkey`. Leave blank if you want to use `msg.authkey` to provide input. + +The encryption security key configured in the edit config will override `msg.privkey`. Leave blank if you want to use `msg.privkey` to provide input. + The oid configured in the edit config will override `msg.oid`. Leave blank if you want to use `msg.oid` to provide input. @@ -109,9 +145,15 @@ Values depends on the oids being requested. Simple SNMP oid subtree fetcher. Triggered by any input. Reads from OID specified and any below it. -`msg.host` may contain the host. +`msg.host` may contain the host including the port. -`msg.community` may contain the community. +`msg.community` may contain the community. (v1 and v2c only) + +`msg.username` may contain the username. (v3 only) + +`msg.authkey` may contain the digest security key. (v3 only) + +`msg.privkey` may contain the encryption security key. (v3 only) `msg.oid` may contain the oid of a single table to search for. @@ -119,6 +161,12 @@ The host configured in the edit config will override `msg.host`. Leave blank if The community configured in the edit config will override `msg.community`. Leave blank if you want to use `msg.community` to provide input. +The username configured in the edit config will override `msg.username`. Leave blank if you want to use `msg.username` to provide input. + +The digest security key configured in the edit config will override `msg.authkey`. Leave blank if you want to use `msg.authkey` to provide input. + +The encryption security key configured in the edit config will override `msg.privkey`. Leave blank if you want to use `msg.privkey` to provide input. + The oid configured in the edit config will override `msg.oid`. Leave blank if you want to use `msg.oid` to provide input. @@ -129,9 +177,15 @@ Values depends on the oids being requested. Simple SNMP oid walker fetcher. Triggered by any input. Reads from OID specified to the end of the table. -`msg.host` may contain the host. +`msg.host` may contain the host including the port. -`msg.community` may contain the community. +`msg.community` may contain the community. (v1 and v2c only) + +`msg.username` may contain the username. (v3 only) + +`msg.authkey` may contain the digest security key. (v3 only) + +`msg.privkey` may contain the encryption security key. (v3 only) `msg.oid` may contain the oid of a single table to search for. @@ -139,6 +193,12 @@ The host configured in the edit config will override `msg.host`. Leave blank if The community configured in the edit config will override `msg.community`. Leave blank if you want to use `msg.community` to provide input. +The username configured in the edit config will override `msg.username`. Leave blank if you want to use `msg.username` to provide input. + +The digest security key configured in the edit config will override `msg.authkey`. Leave blank if you want to use `msg.authkey` to provide input. + +The encryption security key configured in the edit config will override `msg.privkey`. Leave blank if you want to use `msg.privkey` to provide input. + The oid configured in the edit config will override `msg.oid`. Leave blank if you want to use `msg.oid` to provide input. diff --git a/io/snmp/package.json b/io/snmp/package.json index c2c118f7..f812391e 100644 --- a/io/snmp/package.json +++ b/io/snmp/package.json @@ -1,9 +1,9 @@ { "name" : "node-red-node-snmp", - "version" : "0.0.25", + "version" : "0.0.26", "description" : "A Node-RED node that looks for SNMP oids.", "dependencies" : { - "net-snmp" : "1.2.4" + "net-snmp" : "2.9.6" }, "repository" : { "type":"git", @@ -23,6 +23,7 @@ }, "contributors": [ { "name": "Mika Karaila" }, - { "name": "Bryan Malyn" } + { "name": "Bryan Malyn" }, + { "name": "Andres" } ] } diff --git a/io/snmp/snmp.html b/io/snmp/snmp.html index 4cd13ef8..0df25340 100644 --- a/io/snmp/snmp.html +++ b/io/snmp/snmp.html @@ -3,19 +3,56 @@ -
- - -
Timeout  S
+
+ + +
+ +
+ + +
+
+ + +
+
+ + + Priv.Prot. + +
+
+ + +
+
+ + +
+
@@ -31,6 +68,9 @@

Simple SNMP oid or oid list fetcher. Triggered by any input.

msg.host may contain the host.

msg.community may contain the community.

+

msg.username may contain the username.

+

msg.authkey may contain the digest security key.

+

msg.privkey may contain the encryption security key.

msg.oid may contain a comma separated list of oids to request. (no spaces)

OIDs must be numeric. iso. is the same a 1.

The node will output msg.payload and msg.oid.

@@ -42,10 +82,16 @@ color: "YellowGreen", defaults: { host: { value: "127.0.0.1" }, - community: { value: "public" }, - version: { value: "1", required: true }, - oids: { value: "" }, + version: { value: "v1", required: true }, timeout: { value: 5 }, + community: { value: "public" }, + username: { value: "" }, + auth: { value: "noAuthNoPriv", required: true }, + authprot: { value: "None", required: true }, + privprot: { value: "None", required: true }, + authkey: { value: "" }, + privkey: { value: "" }, + oids: { value: "" }, name: { value: "" } }, inputs: 1, @@ -65,19 +111,58 @@
-
- - -
Timeout  S
+
+ + +
+ +
+ + +
+
+ + +
+
+ + + Priv.Prot. + +
+
+ + +
+
+ + +
+
@@ -179,18 +228,18 @@

Simple snmp Set node. Trigger by any input

msg.host may contain the host.

msg.community may contain the community.

-

msg.username may contain the username.

-

msg.authkey may contain the digest security key.

-

msg.privkey may contain the encryption security key.

+

msg.username may contain the username. (V3 only)

+

msg.authkey may contain the digest security key. (V3 only)

+

msg.privkey may contain the encryption security key. (V3 only)

msg.varbinds may contain varbinds as an array of json objects containing multiple oids, types and values. -

[
-    {
-        "oid":   "1.3.6.1.2.1.1.5.0",
-        "type":  "OctetString",
-        "value": "host1"
-    },
-    {   "oid":   ... }
-]
+
[
+  {
+    "oid": "1.3.6.1.2.1.1.5.0",
+    "type": "OctetString",
+    "value": "host1"
+  },
+  { "oid": ... }
+]

Any numeric inputs must be numbers, not strings, e.g. 1 not "1".

OIDs must be numeric. iso. is the same a 1.

@@ -205,15 +254,24 @@ version: { value: "v1", required: true }, timeout: { value: 5 }, community: { value: "public" }, - username: { value: "" }, auth: { value: "noAuthNoPriv", required: true }, - authprot: { value: "None", required: true }, - privprot: { value: "None", required: true }, - authkey: { value: "" }, - privkey: { value: "" }, - varbinds: { value: "" }, + authprot: { value: "MD5", required: true }, + privprot: { value: "DES", required: true }, + oids: { value: "" }, + varbinds: { value: "", validate:function(v) { + try { + return !v || !!JSON.parse(v); + } catch(e) { + return false; + } + }}, name: { value: "" } }, + credentials: { + username: { type: "text" }, + authkey: { type: "password" }, + privkey: { type: "password" } + }, inputs: 1, outputs: 0, icon: "snmp.png", @@ -222,6 +280,9 @@ }, labelStyle: function () { return this.name ? "node_label_italic" : ""; + }, + oneditprepare: function () { + node_snmp_common.oneditprepare(this); } }); @@ -243,43 +304,43 @@ Timeout  S
-
+
-
+
-
- +
+
-
- +
+ - Priv.Prot. +
+
+ + +
+
+
-
- - -
-
- +
+
@@ -298,9 +359,9 @@

Simple SNMP oid table fetcher. Triggered by any input.

msg.host may contain the host.

msg.community may contain the community.

-

msg.username may contain the username.

-

msg.authkey may contain the digest security key.

-

msg.privkey may contain the encryption security key.

+

msg.username may contain the username. (V3 only)

+

msg.authkey may contain the digest security key. (V3 only)

+

msg.privkey may contain the encryption security key. (V3 only)

msg.oid may contain the oid of a table to request.

OID must be numeric. iso. is the same a 1.

The node will output msg.payload and msg.oid.

@@ -315,15 +376,17 @@ version: { value: "v1", required: true }, timeout: { value: 5 }, community: { value: "public" }, - username: { value: "" }, auth: { value: "noAuthNoPriv", required: true }, - authprot: { value: "None", required: true }, - privprot: { value: "None", required: true }, - authkey: { value: "" }, - privkey: { value: "" }, + authprot: { value: "MD5", required: true }, + privprot: { value: "DES", required: true }, oids: { value: "" }, name: { value: "" } }, + credentials: { + username: { type: "text" }, + authkey: { type: "password" }, + privkey: { type: "password" } + }, inputs: 1, outputs: 1, icon: "snmp.png", @@ -332,6 +395,9 @@ }, labelStyle: function () { return this.name ? "node_label_italic" : ""; + }, + oneditprepare: function () { + node_snmp_common.oneditprepare(this); } }); @@ -353,43 +419,43 @@ Timeout  S
-
+
-
+
-
- +
+
-
- +
+ - Priv.Prot. +
+
+ + +
+
+
-
- - -
-
- +
+
@@ -408,9 +474,9 @@

Simple SNMP oid subtree fetcher. Triggered by any input. Reads all OIDS at and below the current base OID.

msg.host may contain the host.

msg.community may contain the community.

-

msg.username may contain the username.

-

msg.authkey may contain the digest security key.

-

msg.privkey may contain the encryption security key.

+

msg.username may contain the username. (V3 only)

+

msg.authkey may contain the digest security key. (V3 only)

+

msg.privkey may contain the encryption security key. (V3 only)

msg.oid may contain the oid of a table to request.

OID must be numeric. iso. is the same a 1.

The node will output msg.payload and msg.oid.

@@ -425,15 +491,17 @@ version: { value: "v1", required: true }, timeout: { value: 5 }, community: { value: "public" }, - username: { value: "" }, auth: { value: "noAuthNoPriv", required: true }, - authprot: { value: "None", required: true }, - privprot: { value: "None", required: true }, - authkey: { value: "" }, - privkey: { value: "" }, + authprot: { value: "MD5", required: true }, + privprot: { value: "DES", required: true }, oids: { value: "" }, name: { value: "" } }, + credentials: { + username: { type: "text" }, + authkey: { type: "password" }, + privkey: { type: "password" } + }, inputs: 1, outputs: 1, icon: "snmp.png", @@ -442,6 +510,9 @@ }, labelStyle: function () { return this.name ? "node_label_italic" : ""; + }, + oneditprepare: function () { + node_snmp_common.oneditprepare(this); } }); @@ -464,43 +535,43 @@ Timeout  S
-
+
-
+
-
- +
+
-
- +
+ - Priv.Prot. +
+
+ + +
+
+
-
- - -
-
- +
+
@@ -520,9 +591,9 @@ Fetches all nodes from this OID to the end of the table.

msg.host may contain the host.

msg.community may contain the community.

-

msg.username may contain the username.

-

msg.authkey may contain the digest security key.

-

msg.privkey may contain the encryption security key.

+

msg.username may contain the username. (V3 only)

+

msg.authkey may contain the digest security key. (V3 only)

+

msg.privkey may contain the encryption security key. (V3 only)

msg.oid may contain the oid of a table to request.

OID must be numeric. iso. is the same a 1.

The node will output msg.payload and msg.oid.

@@ -539,15 +610,17 @@ version: { value: "v1", required: true }, timeout: { value: 5 }, community: { value: "public" }, - username: { value: "" }, auth: { value: "noAuthNoPriv", required: true }, - authprot: { value: "None", required: true }, - privprot: { value: "None", required: true }, - authkey: { value: "" }, - privkey: { value: "" }, + authprot: { value: "MD5", required: true }, + privprot: { value: "DES", required: true }, oids: { value: "" }, name: { value: "" } }, + credentials: { + username: { type: "text" }, + authkey: { type: "password" }, + privkey: { type: "password" } + }, inputs: 1, outputs: 1, icon: "snmp.png", @@ -556,6 +629,9 @@ }, labelStyle: function () { return this.name ? "node_label_italic" : ""; + }, + oneditprepare: function () { + node_snmp_common.oneditprepare(this); } }); diff --git a/io/snmp/snmp.js b/io/snmp/snmp.js index 29fd102e..501b4af9 100644 --- a/io/snmp/snmp.js +++ b/io/snmp/snmp.js @@ -1,429 +1,479 @@ module.exports = function (RED) { "use strict"; - var snmp = require("net-snmp"); - - var sessions = {}; - - function openSession(host, data, options) { - //console.log({data}); - //console.log({options}); - var sessionid = data.sessionid; - options.port = 161; - if (host.indexOf(":") !== -1) { - options.port = host.split(":")[1]; - host = host.split(":")[0]; - } - // SNMPv3 call - if (options.version === "v3"){ - var user = {}; - options.version = snmp.Version3; - user.name = data.name || ""; - user.level = snmp.SecurityLevel.noAuthNoPriv; - if (data.auth === "authNoPriv" || data.auth === "authPriv" ) { - user.level = snmp.SecurityLevel.authNoPriv; - user.authKey = data.authkey || ""; - user.authProtocol = (data.authprot === "SHA") ? snmp.AuthProtocols.sha : snmp.AuthProtocols.md5; - if (data.auth === "authPriv" ) { - user.level = snmp.SecurityLevel.authPriv; - if (data.privprot === "DES" || data.privprot === "AES"){ - user.privProtocol = (data.privprot === "AES") ? snmp.PrivProtocols.aes : snmp.PrivProtocols.des; - user.privKey = data.privkey || ""; - } - } + const SNMP = require("net-snmp"); + const sessions = {}; + function generateUUID() { + let d = Date.now(); + let d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now() * 1000)) || (Date.now() * Math.random() * 100000);//Time in microseconds since load + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + let r = Math.random() * 16;//random number between 0 and 16 + if (d > 0) {//Use timestamp until depleted + r = (d + r) % 16 | 0; + d = Math.floor(d / 16); + } else {//Use microseconds since page-load if supported + r = (d2 + r) % 16 | 0; + d2 = Math.floor(d2 / 16); } - sessions[sessionid] = snmp.createV3Session(host, user, options); + return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); + }); + } + function openSession(sessionid, host, user, options) { + // SNMPv3 call + if (options.version === SNMP.Version3) { + sessions[sessionid] = SNMP.createV3Session(host, user, options); } // SNMPv1 or SNMPv2c call - else{ - var community = data.community; - options.version = (options.version === "v2c") ? snmp.Version2c : snmp.Version1; - sessions[sessionid] = snmp.createSession(host, community, options); + else { + sessions[sessionid] = SNMP.createSession(host, user.community, options); } return sessions[sessionid]; } // Any session needs to be closed after completion function closeSession(sessionid) { - //console.log("closing session"); - sessions[sessionid].close(); + try { + sessions[sessionid].removeAllListeners(); + } catch (e) { } + try { + sessions[sessionid].close(); + } catch (e) { } + delete sessions[sessionid]; } - function SnmpNode(n) { - RED.nodes.createNode(this, n); - this.community = n.community; - this.host = n.host; - this.version = n.version; - this.username = n.username; - this.auth = n.auth; - this.authprot = n.authprot; - this.privprot = n.privprot; - this.authkey = n.authkey; - this.privkey = n.privkey; - this.oids = n.oids.replace(/\s/g, ""); - this.timeout = Number(n.timeout || 5) * 1000; - var node = this; + function initSnmpNode(node, config) { + node.community = config.community; + node.host = config.host; + node.version = config.version; + node.auth = config.auth; + node.authprot = config.authprot; + node.privprot = config.privprot; + if (node.credentials) { + node.username = node.credentials.username; + node.authkey = node.credentials.authkey; + node.privkey = node.credentials.privkey; + } + node.timeout = Number(config.timeout || 5) * 1000; + } - this.on("input", function (msg) { - var host = node.host || msg.host; - var version = node.version; - var community = node.community || msg.community; - var username = node.username || msg.username; - var auth = node.auth; - var authprot = node.authprot; - var privprot = node.privprot; - var authkey = node.authkey || msg.authkey; - var privkey = node.privkey || msg.privkey; - var oids = node.oids || msg.oid; - var sessionid = Date.now(); // Create an unique session ID for each call - var data = {}; - data.community = community; - data.name = username; - data.auth = auth; - data.authprot = authprot; - data.privprot = privprot; - data.authkey = authkey; - data.privkey = privkey; - data.sessionid = sessionid; - var options = {}; - options.version = version; - options.timeout = node.timeout; + function prepareSnmpOptions(node, msg) { + let host = node.host || msg.host; + const sessionid = generateUUID(); + const user = {} + const options = {}; + + options.version = node.version; + if (node.version === "v1") { + options.version = SNMP.Version1; + user.community = node.community || msg.community; + } else if (node.version === "v2c") { + options.version = SNMP.Version2c; + user.community = node.community || msg.community; + } else if (node.version === "v3") { + user.name = node.username || msg.username || ""; + user.level = SNMP.SecurityLevel.noAuthNoPriv; + user.authProtocol = SNMP.AuthProtocols.none; + user.authKey = ""; + user.privProtocol = SNMP.PrivProtocols.none; + user.privKey = ""; + options.version = SNMP.Version3; + if (node.auth === "authNoPriv" || node.auth === "authPriv") { + user.level = SNMP.SecurityLevel.authNoPriv; + user.authProtocol = (node.authprot === "SHA") ? SNMP.AuthProtocols.sha : SNMP.AuthProtocols.md5; + user.authKey = node.authkey || msg.authkey || ""; + if (node.auth === "authPriv") { + user.level = SNMP.SecurityLevel.authPriv; + if (node.privprot === "DES" || node.privprot === "AES") { + user.privProtocol = (node.privprot === "AES") ? SNMP.PrivProtocols.aes : SNMP.PrivProtocols.des; + user.privKey = node.privkey || msg.privkey || ""; + } + } + } + } + + options.timeout = node.timeout; + options.debug = msg.debug || undefined; + options.port = options.port || 161; + options.retries = options.retries || 1; + + if (msg.engineID) { + options.engineID = msg.engineID;//The engineID used for SNMPv3 communications, given as a hex string - defaults to a system-generated engineID containing elements of random + } + if (msg.backoff) { + options.backoff = msg.backoff;//The factor by which to increase the timeout for every retry, defaults to 1 for no increase + } + if (msg.backwardsGetNexts) { + options.backwardsGetNexts = msg.backwardsGetNexts;//boolean to allow GetNext operations to retrieve lexicographically preceding OIDs + } + if (msg.idBitsSize === 16 || msg.idBitsSize === 32) { + options.idBitsSize = msg.idBitsSize;//Either 16 or 32, defaults to 32. Used to reduce the size of the generated id for compatibility with some older devices. + } + const ipv = parseIP(host); + if (ipv.version === 4) { + host = ipv.ip; + options.port = ipv.port || options.port; + options.transport = 'udp4'; + } else if (ipv.version === 6) { + host = ipv.ip; + options.port = ipv.port || options.port; + options.transport = 'udp6'; + } else { + //probably a host name + if (host.indexOf(":") > 0) { + host = host.split(":")[0]; + options.port = host.split(":")[1]; + } + } + return { + host: host, + sessionid: sessionid, + user: user, + options: options, + } + } + function parseIP(ip) { + const IPV4_PAT = /^(\d+)\.(\d+)\.(\d+)\.(\d+)(?::(\d+)){0,1}$/g; + const IPV6_DOUBLE_COL_PAT = /^\[{0,1}([0-9a-f:]*)::([0-9a-f:]*)(?:\]:(\d+)){0,1}$/g; + const ipv4Matcher = IPV4_PAT.exec(ip); + let hex = ""; + let port = undefined; + let ipOnly = []; + try { + + if (ipv4Matcher && ipv4Matcher.length) { + for (let i = 1; i <= 4; i++) { + ipOnly.push(ipv4Matcher[i]); + hex += toHex4(ipv4Matcher[i]); + } + if (ipv4Matcher[5]) { + port = parseInt(ipv4Matcher[5]); + } + return { ip: ipOnly.join("."), hex, port, version: 4 }; + } + + // IPV6 Must be colons format (a:b:c:d:e:A.B.C.D not currently supported) + let ipv6Pattern = "^\\[{0,1}"; + for (let i = 1; i <= 7; i++) { + ipv6Pattern += "([0-9a-f]+):"; + } + ipv6Pattern += "([0-9a-f]+)(?:\\]:(\\d+)){0,1}$"; + const IPV6_PAT = new RegExp(ipv6Pattern); + + + // IPV6, double colon + const ipv6DoubleColonMatcher = IPV6_DOUBLE_COL_PAT.exec(ip); + if (ipv6DoubleColonMatcher && ipv6DoubleColonMatcher.length) { + let p1 = ipv6DoubleColonMatcher[1]; + if (!p1) { + p1 = "0"; + } + let p2 = ipv6DoubleColonMatcher[2]; + if (!p2) { + p2 = "0"; + } + p1 = p1.padStart(4, "0"); + p2 = p2.padStart(4, "0"); + ip = p1 + getZeros(8 - numCount(p1) - numCount(p2)) + p2; + if (ipv6DoubleColonMatcher[3]) { + ip = "[" + ip + "]:" + ipv6DoubleColonMatcher[3]; + } + } + + // IPV6 + const ipv6Matcher = IPV6_PAT.exec(ip); + if (ipv6Matcher && ipv6Matcher.length) { + for (let i = 1; i <= 8; i++) { + const p = toHex6(ipv6Matcher[i]).padStart(4, "0"); + ipOnly.push(p); + hex += p; + } + if (ipv6Matcher[9]) { + port = parseInt(ipv6Matcher[9]); + } + return { ip: ipOnly.join(":"), hex, port, version: 6 }; + } + + throw new Error("Unknown address: " + ip); + } catch (error) { + return { ip, hex, port, version: null, error: error }; + } + + function numCount(/** @type {string} */s) { + return s.split(":").length; + } + function getZeros(/** @type {number} */ count) { + const sb = [":"]; + while (count > 0) { + sb.push("0000:"); + count--; + } + return sb.join(""); + } + function toHex4(/** @type {string} */ s) { + const val = parseInt(s); + if (val < 0 || val > 255) { + throw new Error("Invalid value : " + s); + } + return val.toString(16).padStart(2, "0"); + } + function toHex6(/** @type {string} */ s) { + const val = parseInt(s, 16); + if (val < 0 || val > 65536) { + throw new Error("Invalid hex value : " + s); + } + return s; + } + } + function SnmpNode(n) { + const node = this; + RED.nodes.createNode(node, n); + initSnmpNode(node, n); + node.oids = n.oids ? n.oids.replace(/\s/g, "") : ""; + + node.on("input", function (msg) { + const oids = node.oids || msg.oid; + const { host, sessionid, user, options } = prepareSnmpOptions(node, msg); if (oids) { - openSession(host, data, options).get(oids.split(","), function (error, varbinds) { + let sess = openSession(sessionid, host, user, options); + sess.on("error", function (err) { + node.error(err, msg); + }) + sess.get(oids.split(","), function (error, varbinds) { if (error) { node.error(error.toString(), msg); - } - else { - for (var i = 0; i < varbinds.length; i++) { - if (snmp.isVarbindError(varbinds[i])) { - node.error(snmp.varbindError(varbinds[i]), msg); + } else { + for (let i = 0; i < varbinds.length; i++) { + let vb = varbinds[i]; + if (SNMP.isVarbindError(vb)) { + node.error(SNMP.varbindError(vb), msg); + vb._error = SNMP.varbindError(vb); //add _error to msg so users can determine the varbind is not valid } else { - if (varbinds[i].type == 4) { varbinds[i].value = varbinds[i].value.toString(); } - varbinds[i].tstr = snmp.ObjectType[varbinds[i].type]; - // node.log(varbinds[i].oid + "|" + varbinds[i].tstr + "|" + varbinds[i].value); + if (vb.type == 4) { vb.value = vb.value.toString(); } } + vb.tstr = SNMP.ObjectType[vb.type]; } - msg.oid = oids; msg.payload = varbinds; + msg.oid = oids; node.send(msg); } closeSession(sessionid); // Needed to close the session else a bad or good read could affect future readings }); - } - else { + } else { node.warn("No oid(s) to search for"); } }); } - RED.nodes.registerType("snmp", SnmpNode); + RED.nodes.registerType("snmp", SnmpNode, { + credentials: { + username: { type: "text" }, + authkey: { type: "password" }, + privkey: { type: "password" } + } + }); function SnmpSNode(n) { - RED.nodes.createNode(this, n); - this.community = n.community; - this.host = n.host; - this.version = n.version; - this.username = n.username; - this.auth = n.auth; - this.authprot = n.authprot; - this.privprot = n.privprot; - this.authkey = n.authkey; - this.privkey = n.privkey; - this.timeout = Number(n.timeout || 5) * 1000; - this.varbinds = n.varbinds; - if (this.varbinds && this.varbinds.trim().length === 0) { delete this.varbinds; } - var node = this; - this.on("input", function (msg) { - var host = node.host || msg.host; - var version = node.version; - var community = node.community || msg.community; - var username = node.username || msg.username; - var auth = node.auth; - var authprot = node.authprot; - var privprot = node.privprot; - var authkey = node.authkey || msg.authkey; - var privkey = node.privkey || msg.privkey; - var sessionid = Date.now(); - var data = {}; - data.community = community; - data.name = username; - data.auth = auth; - data.authprot = authprot; - data.privprot = privprot; - data.authkey = authkey; - data.privkey = privkey; - data.sessionid = sessionid; - var options = {}; - options.version = version; - options.timeout = node.timeout; - var varbinds = (node.varbinds) ? JSON.parse(node.varbinds) : msg.varbinds; + const node = this; + RED.nodes.createNode(node, n); + initSnmpNode(node, n); + node.varbinds = n.varbinds; + if (node.varbinds && node.varbinds.trim().length === 0) { delete node.varbinds; } + node.on("input", function (msg) { + const { host, sessionid, user, options } = prepareSnmpOptions(node, msg); + const varbinds = (node.varbinds) ? JSON.parse(node.varbinds) : msg.varbinds; if (varbinds) { - for (var i = 0; i < varbinds.length; i++) { - varbinds[i].type = snmp.ObjectType[varbinds[i].type]; + for (let i = 0; i < varbinds.length; i++) { + varbinds[i].type = SNMP.ObjectType[varbinds[i].type]; } - openSession(host, data, options).set(varbinds, function (error, varbinds) { + let sess = openSession(sessionid, host, user, options); + sess.on("error", function (err) { + node.error(err, msg); + }) + sess.set(varbinds, function (error, varbinds) { if (error) { node.error(error.toString(), msg); - } - else { - for (var i = 0; i < varbinds.length; i++) { + } else { + for (let i = 0; i < varbinds.length; i++) { // for version 2c we must check each OID for an error condition - if (snmp.isVarbindError(varbinds[i])) { - node.error(snmp.varbindError(varbinds[i]), msg); + if (SNMP.isVarbindError(varbinds[i])) { + node.error(SNMP.varbindError(varbinds[i]), msg); } } } closeSession(sessionid); }); - } - else { + } else { node.warn("No varbinds to set"); } }); } - RED.nodes.registerType("snmp set", SnmpSNode); + RED.nodes.registerType("snmp set", SnmpSNode, { + credentials: { + username: { type: "text" }, + authkey: { type: "password" }, + privkey: { type: "password" } + } + }); function SnmpTNode(n) { - RED.nodes.createNode(this, n); - this.community = n.community; - this.host = n.host; - this.version = n.version; - this.username = n.username; - this.auth = n.auth; - this.authprot = n.authprot; - this.privprot = n.privprot; - this.authkey = n.authkey; - this.privkey = n.privkey; - this.oids = n.oids.replace(/\s/g, ""); - this.timeout = Number(n.timeout || 5) * 1000; - var node = this; - var maxRepetitions = 20; + const node = this; + RED.nodes.createNode(node, n); + initSnmpNode(node, n); + node.oids = n.oids ? n.oids.replace(/\s/g, "") : "" + const maxRepetitions = 20; function sortInt(a, b) { if (a > b) { return 1; } - else if (b > a) { return -1; } - else { return 0; } + else if (b > a) { return -1; } else { return 0; } } - this.on("input", function (msg) { - var host = node.host || msg.host; - var version = node.version; - var community = node.community || msg.community; - var username = node.username || msg.username; - var auth = node.auth; - var authprot = node.authprot; - var privprot = node.privprot; - var authkey = node.authkey || msg.authkey; - var privkey = node.privkey || msg.privkey; - var oids = node.oids || msg.oid; - var sessionid = Date.now(); - var data = {}; - data.community = community; - data.name = username; - data.auth = auth; - data.authprot = authprot; - data.privprot = privprot; - data.authkey = authkey; - data.privkey = privkey; - data.sessionid = sessionid; - var options = {}; - options.version = version; - options.timeout = node.timeout; - node.log({options}); + node.on("input", function (msg) { + const oids = node.oids || msg.oid; + const { host, sessionid, user, options } = prepareSnmpOptions(node, msg); if (oids) { msg.oid = oids; - openSession(host, data, options).table(oids, maxRepetitions, function (error, table) { + let sess = openSession(sessionid, host, user, options); + sess.on("error", function (err) { + node.error(err, msg); + }) + sess.table(oids, maxRepetitions, function (error, table) { if (error) { node.error(error.toString(), msg); - } - else { - var indexes = []; - for (var index in table) { + } else { + const indexes = []; + for (let index in table) { if (table.hasOwnProperty(index)) { indexes.push(parseInt(index)); } } indexes.sort(sortInt); - for (var i = 0; i < indexes.length; i++) { - var columns = []; - for (var column in table[indexes[i]]) { + for (let i = 0; i < indexes.length; i++) { + const columns = []; + for (let column in table[indexes[i]]) { if (table[indexes[i]].hasOwnProperty(column)) { columns.push(parseInt(column)); } } columns.sort(sortInt); - // console.log("row index = " + indexes[i]); - // for (var j = 0; j < columns.length; j++) { - // console.log(" column " + columns[j] + " = " + table[indexes[i]][columns[j]]); - // } } msg.payload = table; node.send(msg); } closeSession(sessionid); }); - } - else { + } else { node.warn("No oid to search for"); } }); } - RED.nodes.registerType("snmp table", SnmpTNode); + RED.nodes.registerType("snmp table", SnmpTNode, { + credentials: { + username: { type: "text" }, + authkey: { type: "password" }, + privkey: { type: "password" } + } + }); function SnmpSubtreeNode(n) { - RED.nodes.createNode(this, n); - this.community = n.community; - this.host = n.host; - this.version = n.version; - this.username = n.username; - this.auth = n.auth; - this.authprot = n.authprot; - this.privprot = n.privprot; - this.authkey = n.authkey; - this.privkey = n.privkey; - this.oids = n.oids.replace(/\s/g, ""); - this.timeout = Number(n.timeout || 5) * 1000; - var node = this; - var maxRepetitions = 20; - var response = []; + const node = this; + RED.nodes.createNode(node, n); + initSnmpNode(node, n); + node.oids = n.oids ? n.oids.replace(/\s/g, "") : "" + const maxRepetitions = 20; - function feedCb(varbinds) { - for (var i = 0; i < varbinds.length; i++) { - if (snmp.isVarbindError(varbinds[i])) { - node.error(snmp.varbindError(varbinds[i]), msg); - } - else { - //console.log(varbinds[i].oid + "|" + varbinds[i].value); - response.push({ oid: varbinds[i].oid, value: varbinds[i].value }); - } - } - } - - this.on("input", function (msg) { - var host = node.host || msg.host; - var version = node.version; - var community = node.community || msg.community; - var username = node.username || msg.username; - var auth = node.auth; - var authprot = node.authprot; - var privprot = node.privprot; - var authkey = node.authkey || msg.authkey; - var privkey = node.privkey || msg.privkey; - var oids = node.oids || msg.oid; - var sessionid = Date.now(); - var data = {}; - data.community = community; - data.name = username; - data.auth = auth; - data.authprot = authprot; - data.privprot = privprot; - data.authkey = authkey; - data.privkey = privkey; - data.sessionid = sessionid; - var options = {}; - options.version = version; - options.timeout = node.timeout; + node.on("input", function (msg) { + const oids = node.oids || msg.oid; + const { host, sessionid, user, options } = prepareSnmpOptions(node, msg); if (oids) { msg.oid = oids; - openSession(host, data, options).subtree(msg.oid, maxRepetitions, feedCb, function (error) { + let sess = openSession(sessionid, host, user, options); + sess.on("error", function (err) { + node.error(err, msg); + }) + //move response array & feedCb to inside `node.on("input",` to avoid subsequent + // calls overwriting results from previous operations (each call gets own result/response) + const response = []; + function feedCb(varbinds) { + for (let i = 0; i < varbinds.length; i++) { + if (SNMP.isVarbindError(varbinds[i])) { + node.error(SNMP.varbindError(varbinds[i]), msg); + } else { + response.push({ oid: varbinds[i].oid, value: varbinds[i].value }); + } + } + } + sess.subtree(msg.oid, maxRepetitions, feedCb, function (error) { if (error) { node.error(error.toString(), msg); - } - else { - // Clone the array - msg.payload = response.slice(0); + } else { + msg.payload = response; node.send(msg); - //Clears response - response.length = 0; } closeSession(sessionid); }); - } - else { + } else { node.warn("No oid to search for"); } }); } - RED.nodes.registerType("snmp subtree", SnmpSubtreeNode); - + RED.nodes.registerType("snmp subtree", SnmpSubtreeNode, { + credentials: { + username: { type: "text" }, + authkey: { type: "password" }, + privkey: { type: "password" } + } + }); function SnmpWalkerNode(n) { - RED.nodes.createNode(this, n); - this.community = n.community; - this.host = n.host; - this.version = n.version; - this.username = n.username; - this.auth = n.auth; - this.authprot = n.authprot; - this.privprot = n.privprot; - this.authkey = n.authkey; - this.privkey = n.privkey; - this.oids = n.oids.replace(/\s/g, ""); - this.timeout = Number(n.timeout || 5) * 1000; - var node = this; - var maxRepetitions = 20; - var response = []; + const node = this; + RED.nodes.createNode(node, n); + initSnmpNode(node, n); + node.oids = n.oids ? n.oids.replace(/\s/g, "") : "" + const maxRepetitions = 20; - function feedCb(varbinds) { - for (var i = 0; i < varbinds.length; i++) { - if (snmp.isVarbindError(varbinds[i])) { - node.error(snmp.varbindError(varbinds[i]), msg); - } - else { - //console.log(varbinds[i].oid + "|" + varbinds[i].value); - response.push({ oid: varbinds[i].oid, value: varbinds[i].value }); - } - } - } - - this.on("input", function (msg) { - node.msg = msg; - var host = node.host || msg.host; - var version = node.version; - var community = node.community || msg.community; - var username = node.username || msg.username; - var auth = node.auth; - var authprot = node.authprot; - var privprot = node.privprot; - var authkey = node.authkey || msg.authkey; - var privkey = node.privkey || msg.privkey; - var oids = node.oids || msg.oid; - var sessionid = Date.now(); - var data = {}; - data.community = community; - data.name = username; - data.auth = auth; - data.authprot = authprot; - data.privprot = privprot; - data.authkey = authkey; - data.privkey = privkey; - data.sessionid = sessionid; - var options = {}; - options.version = version; - options.timeout = node.timeout; + node.on("input", function (msg) { + const oids = node.oids || msg.oid; + const { host, sessionid, user, options } = prepareSnmpOptions(node, msg); if (oids) { msg.oid = oids; - openSession(host, data, options).walk(msg.oid, maxRepetitions, feedCb, function (error) { + let sess = openSession(sessionid, host, user, options); + sess.on("error", function (err) { + node.error(err, msg); + }) + //move response array & feedCb to inside `node.on("input",` to avoid subsequent + // calls overwriting results from previous operations (each call gets own result/response) + const response = []; + function feedCb(varbinds) { + for (let i = 0; i < varbinds.length; i++) { + if (SNMP.isVarbindError(varbinds[i])) { + node.error(SNMP.varbindError(varbinds[i]), msg); + } else { + response.push({ oid: varbinds[i].oid, value: varbinds[i].value }); + } + } + } + sess.walk(msg.oid, maxRepetitions, feedCb, function (error) { if (error) { node.error(error.toString(), msg); - } - else { - // Clone the array - msg.payload = response.slice(0); + } else { + msg.payload = response; node.send(msg); - //Clears response - response.length = 0; } closeSession(sessionid); }); - } - else { + } else { node.warn("No oid to search for"); } }); } - RED.nodes.registerType("snmp walker", SnmpWalkerNode); + RED.nodes.registerType("snmp walker", SnmpWalkerNode, { + credentials: { + username: { type: "text" }, + authkey: { type: "password" }, + privkey: { type: "password" } + } + }); }; From 069ee071ad0cf11d3d80b69b4ac8d08df044ec87 Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Tue, 26 Apr 2022 14:49:23 +0100 Subject: [PATCH 04/86] bump dep to 3.6.3 to handle "no callback" issue - for issue https://github.com/markabrahams/node-net-snmp/issues/202 --- io/snmp/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io/snmp/package.json b/io/snmp/package.json index e8d1472b..ad17112b 100644 --- a/io/snmp/package.json +++ b/io/snmp/package.json @@ -3,7 +3,7 @@ "version" : "0.1.0", "description" : "A Node-RED node that looks for SNMP oids.", "dependencies" : { - "net-snmp" : "3.6.2" + "net-snmp" : "^3.6.3" }, "repository" : { "type":"git", From 484c4f9574dece7ff6468b4847381ce4d5b569d2 Mon Sep 17 00:00:00 2001 From: Steve-Mcl Date: Thu, 28 Apr 2022 20:36:53 +0100 Subject: [PATCH 05/86] Update readme & bump to v1.0.0 --- io/snmp/README.md | 9 +++++++-- io/snmp/package.json | 6 +++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/io/snmp/README.md b/io/snmp/README.md index 8caa1b28..41e26cd7 100644 --- a/io/snmp/README.md +++ b/io/snmp/README.md @@ -1,8 +1,13 @@ node-red-node-snmp ================== -A pair of Node-RED nodes that -fetch either individual oids, or a table oid from a SNMP enabled host. +A set of Node-RED nodes that +fetch values from SNMP enabled hosts. Supports v1, v2c and v3. +* SNMP get - Simple SNMP oid or oid list fetcher +* SNMP set - Simple snmp Set node. +* SNMP subtree - Simple sub tree fetcher +* SNMP table - Simple SNMP oid table fetcher +* SNMP walker - Simple SNMP oid walker fetcher Install ------- diff --git a/io/snmp/package.json b/io/snmp/package.json index ad17112b..ac40df13 100644 --- a/io/snmp/package.json +++ b/io/snmp/package.json @@ -1,7 +1,7 @@ { "name" : "node-red-node-snmp", - "version" : "0.1.0", - "description" : "A Node-RED node that looks for SNMP oids.", + "version" : "1.0.0", + "description" : "A Node-RED node that gets and sets SNMP oid values. Supports v1, v2c and v3", "dependencies" : { "net-snmp" : "^3.6.3" }, @@ -10,7 +10,7 @@ "url":"https://github.com/node-red/node-red-nodes/tree/master/io/snmp" }, "license": "Apache-2.0", - "keywords": [ "node-red", "snmp", "oid" ], + "keywords": [ "node-red", "snmp", "oid", "snmpv3" ], "node-red" : { "nodes" : { "snmp": "snmp.js" From 041342d5114a16b50607ee184ad721297109826c Mon Sep 17 00:00:00 2001 From: Stephen McLaughlin <44235289+Steve-Mcl@users.noreply.github.com> Date: Sat, 7 May 2022 16:40:57 +0100 Subject: [PATCH 06/86] better inplace upgrade of pre v1 (#908) fixes #907 --- io/snmp/package.json | 2 +- io/snmp/snmp.html | 54 ++++++++++++++++++++++++++------------------ io/snmp/snmp.js | 13 +++++++---- 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/io/snmp/package.json b/io/snmp/package.json index f1361253..f2023d0f 100644 --- a/io/snmp/package.json +++ b/io/snmp/package.json @@ -1,6 +1,6 @@ { "name" : "node-red-node-snmp", - "version" : "1.0.0", + "version" : "1.0.1", "description" : "A Node-RED node that gets and sets SNMP oid values. Supports v1, v2c and v3", "dependencies" : { "net-snmp" : "^3.6.3" diff --git a/io/snmp/snmp.html b/io/snmp/snmp.html index f25eec46..f952e331 100644 --- a/io/snmp/snmp.html +++ b/io/snmp/snmp.html @@ -12,14 +12,20 @@ + \ No newline at end of file diff --git a/social/email/61-email.js b/social/email/61-email.js index f1ac8a89..94052d76 100644 --- a/social/email/61-email.js +++ b/social/email/61-email.js @@ -19,6 +19,7 @@ module.exports = function(RED) { var simpleParser = require("mailparser").simpleParser; var SMTPServer = require("smtp-server").SMTPServer; //var microMTA = require("micromta").microMTA; + var fs = require('fs'); if (parseInt(process.version.split("v")[1].split(".")[0]) < 8) { throw "Error : Requires nodejs version >= 8."; @@ -565,47 +566,81 @@ module.exports = function(RED) { function EmailMtaNode(n) { - RED.nodes.createNode(this,n); + RED.nodes.createNode(this, n); this.port = n.port; + this.secure = n.secure; + this.starttls = n.starttls; + this.certFile = n.certFile; + this.keyFile = n.keyFile; + this.users = n.users; + this.auth = n.auth; + try { + this.options = JSON.parse(n.expert); + } catch (error) { + this.options = {}; + } var node = this; + if (!Array.isArray(node.options.disabledCommands)) { + node.options.disabledCommands = []; + } + node.options.secure = node.secure; + if (node.certFile) { + node.options.cert = fs.readFileSync(node.certFile); + } + if (node.keyFile) { + node.options.key = fs.readFileSync(node.keyFile); + } + if (!node.starttls) { + node.options.disabledCommands.push("STARTTLS"); + } + if (!node.auth) { + node.options.disabledCommands.push("AUTH"); + } - node.mta = new SMTPServer({ - secure: false, - logger: false, - disabledCommands: ['AUTH', 'STARTTLS'], - - onData: function (stream, session, callback) { - simpleParser(stream, { skipTextToHtml:true, skipTextLinks:true }, (err, parsed) => { - if (err) { node.error(RED._("email.errors.parsefail"),err); } - else { - node.status({fill:"green", shape:"dot", text:""}); - var msg = {} - msg.payload = parsed.text; - msg.topic = parsed.subject; - msg.date = parsed.date; - msg.header = {}; - parsed.headers.forEach((v, k) => {msg.header[k] = v;}); - if (parsed.html) { msg.html = parsed.html; } - if (parsed.to) { - if (typeof(parsed.to) === "string" && parsed.to.length > 0) { msg.to = parsed.to; } - else if (parsed.to.hasOwnProperty("text") && parsed.to.text.length > 0) { msg.to = parsed.to.text; } - } - if (parsed.cc) { - if (typeof(parsed.cc) === "string" && parsed.cc.length > 0) { msg.cc = parsed.cc; } - else if (parsed.cc.hasOwnProperty("text") && parsed.cc.text.length > 0) { msg.cc = parsed.cc.text; } - } - if (parsed.cc && parsed.cc.length > 0) { msg.cc = parsed.cc; } - if (parsed.bcc && parsed.bcc.length > 0) { msg.bcc = parsed.bcc; } - if (parsed.from && parsed.from.value && parsed.from.value.length > 0) { msg.from = parsed.from.value[0].address; } - if (parsed.attachments) { msg.attachments = parsed.attachments; } - else { msg.attachments = []; } - node.send(msg); // Propagate the message down the flow - setTimeout(function() { node.status({})}, 500); + node.options.onData = function (stream, session, callback) { + simpleParser(stream, { skipTextToHtml:true, skipTextLinks:true }, (err, parsed) => { + if (err) { node.error(RED._("email.errors.parsefail"),err); } + else { + node.status({fill:"green", shape:"dot", text:""}); + var msg = {} + msg.payload = parsed.text; + msg.topic = parsed.subject; + msg.date = parsed.date; + msg.header = {}; + parsed.headers.forEach((v, k) => {msg.header[k] = v;}); + if (parsed.html) { msg.html = parsed.html; } + if (parsed.to) { + if (typeof(parsed.to) === "string" && parsed.to.length > 0) { msg.to = parsed.to; } + else if (parsed.to.hasOwnProperty("text") && parsed.to.text.length > 0) { msg.to = parsed.to.text; } } - callback(); - }); + if (parsed.cc) { + if (typeof(parsed.cc) === "string" && parsed.cc.length > 0) { msg.cc = parsed.cc; } + else if (parsed.cc.hasOwnProperty("text") && parsed.cc.text.length > 0) { msg.cc = parsed.cc.text; } + } + if (parsed.cc && parsed.cc.length > 0) { msg.cc = parsed.cc; } + if (parsed.bcc && parsed.bcc.length > 0) { msg.bcc = parsed.bcc; } + if (parsed.from && parsed.from.value && parsed.from.value.length > 0) { msg.from = parsed.from.value[0].address; } + if (parsed.attachments) { msg.attachments = parsed.attachments; } + else { msg.attachments = []; } + node.send(msg); // Propagate the message down the flow + setTimeout(function() { node.status({})}, 500); + } + callback(); + }); + } + + node.options.onAuth = function (auth, session, callback) { + let id = node.users.findIndex(function (item) { + return item.name === auth.username; + }); + if (id >= 0 && node.users[id].password === auth.password) { + callback(null, { user: id + 1 }); + } else { + callback(new Error("Invalid username or password")); } - }); + } + + node.mta = new SMTPServer(node.options); node.mta.listen(node.port); diff --git a/social/email/locales/en-US/61-email.html b/social/email/locales/en-US/61-email.html index aca1ea6b..9bd97cda 100644 --- a/social/email/locales/en-US/61-email.html +++ b/social/email/locales/en-US/61-email.html @@ -58,11 +58,19 @@ diff --git a/social/email/locales/en-US/61-email.json b/social/email/locales/en-US/61-email.json index 9d876117..67567904 100644 --- a/social/email/locales/en-US/61-email.json +++ b/social/email/locales/en-US/61-email.json @@ -34,7 +34,16 @@ "never": "never", "required": "if required", "always": "always", - "rejectUnauthorised": "Check server certificate is valid" + "rejectUnauthorised": "Check server certificate is valid", + "enableSecure": "Secure connection", + "enableStarttls": "Start TLS", + "starttlsUpgrade": "Upgrade cleartext connection with STARTTLS", + "certFile": "Certificate", + "keyFile":"Private key", + "users": "Users", + "auth": "Authenticate users", + "addButton": "Add", + "expert": "Expert" }, "default-message": "__description__\n\nFile from Node-RED is attached: __filename__", "tip": { From 13b839cfda163f6efcc4c0bf0eec6403b8a62ace Mon Sep 17 00:00:00 2001 From: David D'Hauwe <43988646+daviddhauwe@users.noreply.github.com> Date: Tue, 14 Jun 2022 23:13:16 +0200 Subject: [PATCH 17/86] Smtp server security note and hide unrelevant fields (#924) * Email Mta Node added security and authentication * Documentation updated * Original formatting restored * email-mta added note for port security hide fields when not relevant --- social/email/61-email.html | 38 ++++++++++++++++++------ social/email/locales/en-US/61-email.html | 3 +- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/social/email/61-email.html b/social/email/61-email.html index 8baadfb4..552e7bbe 100644 --- a/social/email/61-email.html +++ b/social/email/61-email.html @@ -316,19 +316,18 @@
-
+
-
+
-
@@ -382,11 +381,18 @@ }, oneditprepare: function () { let node = this; - // Expert settings - $("#node-input-expert").typedInput({ - type: "json", - types: ["json"] - }) + // Certificate settings + $("#node-input-secure").change(secVisibility); + $("#node-input-starttls").change(secVisibility); + function secVisibility() { + if ($("#node-input-secure").is(":checked") || $("#node-input-starttls").is(":checked")) { + $("#certRow").show(); + $("#keyRow").show(); + } else { + $("#certRow").hide(); + $("#keyRow").hide(); + } + } // User Management let cacheItemCount = 0; if (node.users && node.users.length > 0) { @@ -461,7 +467,21 @@ $("#node-input-email-users-container-div").scrollTop( $("#node-input-email-users-container-div").get(0).scrollHeight ); - }); + }); + $("#node-input-auth").change(function () { + if ($("#node-input-auth").is(":checked")) { + $("#node-input-email-users-add").show(); + $("#node-input-email-users-container-div").show(); + } else { + $("#node-input-email-users-add").hide(); + $("#node-input-email-users-container-div").hide(); + } + }); + // Expert settings + $("#node-input-expert").typedInput({ + type: "json", + types: ["json"] + }) }, oneditsave: function () { let node = this; diff --git a/social/email/locales/en-US/61-email.html b/social/email/locales/en-US/61-email.html index 9bd97cda..1eb79661 100644 --- a/social/email/locales/en-US/61-email.html +++ b/social/email/locales/en-US/61-email.html @@ -68,7 +68,8 @@

Security

When Secure connection is checked, the connection will use TLS. If not it is still possible to upgrade clear text socket to TLS socket by checking Start TLS. - If you do no specify your own certificate (path to file) then a pregenerated self-signed certificate is used. Any respectful client refuses to accept such certificate.

+ In most cases when using port 465, check Secure connection. For port 587 or 25 keep it disabled, use Start TLS instead.

+

If you do no specify your own certificate (path to file) then a pregenerated self-signed certificate is used. Any respectful client refuses to accept such certificate.

Authentication

Authentication can be enabled (PLAIN or LOGIN). Add at least one user.

Expert

From 8431e624f7d6e31dde62bf11a8723f25a0e94953 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Wed, 29 Jun 2022 10:01:16 +0100 Subject: [PATCH 18/86] Bump email node dependencies --- social/email/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/social/email/package.json b/social/email/package.json index 62046064..da850414 100644 --- a/social/email/package.json +++ b/social/email/package.json @@ -1,12 +1,12 @@ { "name": "node-red-node-email", - "version": "1.15.1", + "version": "1.16.0", "description": "Node-RED nodes to send and receive simple emails.", "dependencies": { "imap": "^0.8.19", - "mailparser": "^3.4.0", - "nodemailer": "^6.7.3", - "smtp-server": "^3.10.0" + "mailparser": "^3.5.0", + "nodemailer": "^6.7.5", + "smtp-server": "^3.11.0" }, "bundledDependencies": [ "imap", From 4cfbc6a32aa3228d1cff0df816f2f3841cd57034 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Wed, 29 Jun 2022 10:06:24 +0100 Subject: [PATCH 19/86] Add resource and separate out domain in XMPP node to address #881 --- social/xmpp/92-xmpp.html | 9 +++++++-- social/xmpp/92-xmpp.js | 10 ++++++++-- social/xmpp/package.json | 2 +- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/social/xmpp/92-xmpp.html b/social/xmpp/92-xmpp.html index d042095a..ab299773 100644 --- a/social/xmpp/92-xmpp.html +++ b/social/xmpp/92-xmpp.html @@ -157,13 +157,17 @@
- +
+
+ + +
diff --git a/hardware/intel/mraa-gpio-din.js b/hardware/intel/mraa-gpio-din.js index 5966e448..cfb79612 100644 --- a/hardware/intel/mraa-gpio-din.js +++ b/hardware/intel/mraa-gpio-din.js @@ -7,13 +7,16 @@ module.exports = function(RED) { RED.nodes.createNode(this,n); this.pin = n.pin; this.interrupt = n.interrupt; + this.mode = n.mode; + this.initialMsg = n.initial; this.x = new m.Gpio(parseInt(this.pin)); this.board = m.getPlatformName(); + this.defaultTimeout = 100; var node = this; - node.x.mode(m.PIN_GPIO); + node.x.mode(parseInt(this.mode)); node.x.dir(m.DIR_IN); - node.x.isr(m.EDGE_BOTH, function() { - var g = node.x.read(); + + var eventHandler = function(g) { var msg = { payload:g, topic:node.board+"/D"+node.pin }; switch (g) { case 0: { @@ -34,8 +37,15 @@ module.exports = function(RED) { node.status({fill:"grey",shape:"ring",text:"unknown"}); } } - }); - switch (node.x.read()) { + } + + var isrCallback = function() { + eventHandler(node.x.read()); + } + + node.x.isr(m.EDGE_BOTH, isrCallback); + var initialState = node.x.read(); + switch (initialState) { case 0: { node.status({fill:"green",shape:"ring",text:"low"}); break; @@ -48,8 +58,17 @@ module.exports = function(RED) { node.status({}); } } + + if (this.initialMsg) { + setTimeout(() => { + node.send( { payload: node.x.read(), topic:node.board+"/D"+node.pin } ); + }, this.defaultTimeout); + } + this.on('close', function() { node.x.isr(m.EDGE_BOTH, null); + node.x.isrExit(); + node.x.close(); }); } RED.nodes.registerType("mraa-gpio-din", gpioDin); From e133d01e5373b2662a78f13b1eb8db4a23729392 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Tue, 12 Jul 2022 10:14:21 +0100 Subject: [PATCH 23/86] bump mraa node for iot2050 PR --- hardware/intel/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hardware/intel/package.json b/hardware/intel/package.json index d360b4e5..0d1c7d5b 100644 --- a/hardware/intel/package.json +++ b/hardware/intel/package.json @@ -1,7 +1,7 @@ { "name" : "node-red-node-intel-gpio", - "version" : "0.0.6", - "description" : "A Node-RED node to talk to an Intel Galileo or Edison using mraa", + "version" : "0.1.0", + "description" : "A Node-RED node to talk to an Intel Galileo, Edison or Siemens IOT2050 board using mraa", "dependencies" : { }, "repository" : { @@ -10,7 +10,7 @@ "directory" : "tree/master/hardware/intel" }, "license": "Apache-2.0", - "keywords": [ "node-red", "intel", "galileo", "edison" ], + "keywords": [ "node-red", "intel", "galileo", "edison", "siemens", "iot2050" ], "node-red" : { "nodes" : { "mraa-gpio-ain": "mraa-gpio-ain.js", From c32823a85a3d227dbbb943d347e5ed497edeed78 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Thu, 14 Jul 2022 11:28:20 +0100 Subject: [PATCH 24/86] pi gpio close pins timeout --- hardware/PiGpio/36-rpi-gpio.js | 16 ++++++++++------ hardware/PiGpio/package.json | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/hardware/PiGpio/36-rpi-gpio.js b/hardware/PiGpio/36-rpi-gpio.js index d93a8a40..7ab000fc 100644 --- a/hardware/PiGpio/36-rpi-gpio.js +++ b/hardware/PiGpio/36-rpi-gpio.js @@ -108,10 +108,12 @@ module.exports = function(RED) { delete pinsInUse[node.pin]; if (node.child != null) { node.finished = done; - node.child.stdin.write("close "+node.pin); - node.child.kill('SIGKILL'); + node.child.stdin.write("close "+node.pin, () => { + node.child.kill('SIGKILL'); + setTimeout(function() { if (done) { done(); } }, 25); + }); } - else { done(); } + else { if (done) { done(); } } }); } RED.nodes.registerType("rpi-gpio in",GPIOInNode); @@ -210,10 +212,12 @@ module.exports = function(RED) { delete pinsInUse[node.pin]; if (node.child != null) { node.finished = done; - node.child.stdin.write("close "+node.pin); - node.child.kill('SIGKILL'); + node.child.stdin.write("close "+node.pin, () => { + node.child.kill('SIGKILL'); + setTimeout(function() { if (done) { done(); } }, 25); + }); } - else { done(); } + else { if (done) { done(); } } }); } diff --git a/hardware/PiGpio/package.json b/hardware/PiGpio/package.json index e445708c..66b71165 100644 --- a/hardware/PiGpio/package.json +++ b/hardware/PiGpio/package.json @@ -1,6 +1,6 @@ { "name": "node-red-node-pi-gpio", - "version": "2.0.3", + "version": "2.0.4", "description": "The basic Node-RED node for Pi GPIO", "dependencies" : { }, From c9f57afe222e361e4ab45d8af99d54cdd56e530a Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Thu, 14 Jul 2022 19:00:58 +0100 Subject: [PATCH 25/86] gpio retry failing pin connection --- hardware/PiGpio/36-rpi-gpio.js | 90 ++++++++++++++++++---------------- hardware/PiGpio/package.json | 2 +- 2 files changed, 50 insertions(+), 42 deletions(-) diff --git a/hardware/PiGpio/36-rpi-gpio.js b/hardware/PiGpio/36-rpi-gpio.js index 7ab000fc..ab7a690a 100644 --- a/hardware/PiGpio/36-rpi-gpio.js +++ b/hardware/PiGpio/36-rpi-gpio.js @@ -45,46 +45,53 @@ module.exports = function(RED) { } } + var startPin = function() { + node.child = spawn(gpioCommand, ["in",node.pin,node.intype,node.debounce]); + node.running = true; + node.status({fill:"yellow",shape:"dot",text:"rpi-gpio.status.ok"}); + + node.child.stdout.on('data', function (data) { + var d = data.toString().trim().split("\n"); + for (var i = 0; i < d.length; i++) { + if (d[i] === '') { return; } + if (node.running && node.buttonState !== -1 && !isNaN(Number(d[i])) && node.buttonState !== d[i]) { + node.send({ topic:"gpio/"+node.pin, payload:Number(d[i]) }); + } + node.buttonState = d[i]; + node.status({fill:"green",shape:"dot",text:d[i]}); + if (RED.settings.verbose) { node.log("out: "+d[i]+" :"); } + } + }); + + node.child.stderr.on('data', function (data) { + if (RED.settings.verbose) { node.log("err: "+data+" :"); } + }); + + node.child.on('close', function (code) { + node.running = false; + node.child.removeAllListeners(); + delete node.child; + if (RED.settings.verbose) { node.log(RED._("rpi-gpio.status.closed")); } + if (!node.finished && code === 1) { + setTimeout(function() {startPin()}, 250); + } + else if (node.finished) { + node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"}); + node.finished(); + } + else { node.status({fill:"red",shape:"ring",text:"rpi-gpio.status.stopped"}); } + }); + + node.child.on('error', function (err) { + if (err.errno === "ENOENT") { node.error(RED._("rpi-gpio.errors.commandnotfound")); } + else if (err.errno === "EACCES") { node.error(RED._("rpi-gpio.errors.commandnotexecutable")); } + else { node.error(RED._("rpi-gpio.errors.error",{error:err.errno})) } + }); + } + if (allOK === true) { if (node.pin !== undefined) { - node.child = spawn(gpioCommand, ["in",node.pin,node.intype,node.debounce]); - node.running = true; - node.status({fill:"yellow",shape:"dot",text:"rpi-gpio.status.ok"}); - - node.child.stdout.on('data', function (data) { - var d = data.toString().trim().split("\n"); - for (var i = 0; i < d.length; i++) { - if (d[i] === '') { return; } - if (node.running && node.buttonState !== -1 && !isNaN(Number(d[i])) && node.buttonState !== d[i]) { - node.send({ topic:"gpio/"+node.pin, payload:Number(d[i]) }); - } - node.buttonState = d[i]; - node.status({fill:"green",shape:"dot",text:d[i]}); - if (RED.settings.verbose) { node.log("out: "+d[i]+" :"); } - } - }); - - node.child.stderr.on('data', function (data) { - if (RED.settings.verbose) { node.log("err: "+data+" :"); } - }); - - node.child.on('close', function (code) { - node.running = false; - node.child = null; - if (RED.settings.verbose) { node.log(RED._("rpi-gpio.status.closed")); } - if (node.finished) { - node.status({fill:"grey",shape:"ring",text:"rpi-gpio.status.closed"}); - node.finished(); - } - else { node.status({fill:"red",shape:"ring",text:"rpi-gpio.status.stopped"}); } - }); - - node.child.on('error', function (err) { - if (err.errno === "ENOENT") { node.error(RED._("rpi-gpio.errors.commandnotfound")); } - else if (err.errno === "EACCES") { node.error(RED._("rpi-gpio.errors.commandnotexecutable")); } - else { node.error(RED._("rpi-gpio.errors.error",{error:err.errno})) } - }); - + startPin(); } else { node.warn(RED._("rpi-gpio.errors.invalidpin")+": "+node.pin); @@ -109,8 +116,9 @@ module.exports = function(RED) { if (node.child != null) { node.finished = done; node.child.stdin.write("close "+node.pin, () => { - node.child.kill('SIGKILL'); - setTimeout(function() { if (done) { done(); } }, 25); + if (node.child) { + node.child.kill('SIGKILL'); + } }); } else { if (done) { done(); } } @@ -214,7 +222,7 @@ module.exports = function(RED) { node.finished = done; node.child.stdin.write("close "+node.pin, () => { node.child.kill('SIGKILL'); - setTimeout(function() { if (done) { done(); } }, 25); + setTimeout(function() { if (done) { done(); } }, 50); }); } else { if (done) { done(); } } diff --git a/hardware/PiGpio/package.json b/hardware/PiGpio/package.json index 66b71165..b9ee921b 100644 --- a/hardware/PiGpio/package.json +++ b/hardware/PiGpio/package.json @@ -1,6 +1,6 @@ { "name": "node-red-node-pi-gpio", - "version": "2.0.4", + "version": "2.0.5", "description": "The basic Node-RED node for Pi GPIO", "dependencies" : { }, From e7c0459a32fcaacb0b95644d3b0fb3bba2a8dd8f Mon Sep 17 00:00:00 2001 From: Sam Machin Date: Fri, 15 Jul 2022 14:12:46 +0100 Subject: [PATCH 26/86] Update sensehat.js (#933) check for sensehat python lib in 2.7 and 3 folders --- hardware/sensehat/sensehat.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardware/sensehat/sensehat.js b/hardware/sensehat/sensehat.js index 67179233..6172c752 100644 --- a/hardware/sensehat/sensehat.js +++ b/hardware/sensehat/sensehat.js @@ -7,7 +7,7 @@ module.exports = function(RED) { var hatCommand = __dirname+'/sensehat'; - if (!fs.existsSync('/usr/lib/python2.7/dist-packages/sense_hat')) { + if (!fs.existsSync('/usr/lib/python2.7/dist-packages/sense_hat') && !fs.existsSync('/usr/lib/python3/dist-packages/sense_hat')) { throw "Error: Can't find Sense HAT python libraries. Run sudo apt-get install sense-hat"; } From 2b6fbcc5b0d15637cb8cb4979c04f8e778f42296 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Fri, 15 Jul 2022 14:13:51 +0100 Subject: [PATCH 27/86] bump sensehat version for python3 check fix --- hardware/sensehat/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardware/sensehat/package.json b/hardware/sensehat/package.json index 23060bbc..20947ada 100644 --- a/hardware/sensehat/package.json +++ b/hardware/sensehat/package.json @@ -1,6 +1,6 @@ { "name" : "node-red-node-pi-sense-hat", - "version" : "0.1.2", + "version" : "0.1.3", "description" : "A Node-RED node to interact with a Raspberry Pi Sense HAT", "repository" : { "type":"git", From 6830ce4fba3263ef60e4625dd8a569dcc97f6d8f Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Tue, 19 Jul 2022 12:59:41 +0100 Subject: [PATCH 28/86] email node - add done capability to retrieving mail node. --- social/email/61-email.js | 35 +++++++++++++++++++++++++---------- social/email/package.json | 4 ++-- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/social/email/61-email.js b/social/email/61-email.js index 94052d76..50a5eb06 100644 --- a/social/email/61-email.js +++ b/social/email/61-email.js @@ -1,5 +1,7 @@ /* eslint-disable indent */ +const { domainToUnicode } = require("url"); + /** * POP3 protocol - RFC1939 - https://www.ietf.org/rfc/rfc1939.txt * @@ -224,7 +226,7 @@ module.exports = function(RED) { } var node = this; - this.interval_id = null; + node.interval_id = null; // Process a new email message by building a Node-RED message to be passed onwards // in the message flow. The parameter called `msg` is the template message we @@ -253,7 +255,7 @@ module.exports = function(RED) { // Check the POP3 email mailbox for any new messages. For any that are found, // retrieve each message, call processNewMessage to process it and then delete // the messages from the server. - function checkPOP3(msg) { + function checkPOP3(msg,send,done) { var currentMessage; var maxMessage; //node.log("Checking POP3 for new messages"); @@ -270,6 +272,7 @@ module.exports = function(RED) { if (currentMessage > maxMessage) { pop3Client.quit(); setInputRepeatTimeout(); + done(); return; } pop3Client.retr(currentMessage); @@ -294,6 +297,7 @@ module.exports = function(RED) { pop3Client.on("error", function(err) { setInputRepeatTimeout(); node.log("error: " + JSON.stringify(err)); + done(); }); pop3Client.on("connect", function() { @@ -309,13 +313,13 @@ module.exports = function(RED) { node.log(util.format("login error: %s %j", status, rawData)); pop3Client.quit(); setInputRepeatTimeout(); + done(); } }); pop3Client.on("retr", function(status, msgNumber, data, rawData) { // node.log(util.format("retr: status=%s, msgNumber=%d, data=%j", status, msgNumber, data)); if (status) { - // We have now received a new email message. Create an instance of a mail parser // and pass in the email message. The parser will signal when it has parsed the message. simpleParser(data, {}, function(err, parsed) { @@ -334,6 +338,7 @@ module.exports = function(RED) { node.log(util.format("retr error: %s %j", status, rawData)); pop3Client.quit(); setInputRepeatTimeout(); + done(); } }); @@ -359,7 +364,7 @@ module.exports = function(RED) { // Check the email sever using the IMAP protocol for new messages. var s = false; var ss = false; - function checkIMAP(msg) { + function checkIMAP(msg,send,done) { //console.log("Checking IMAP for new messages"); // We get back a 'ready' event once we have connected to imap s = true; @@ -392,6 +397,7 @@ module.exports = function(RED) { imap.end(); s = false; setInputRepeatTimeout(); + done(err); return; } else { @@ -407,6 +413,7 @@ module.exports = function(RED) { imap.end(); s = false; setInputRepeatTimeout(); + done(err); return; } else { @@ -417,6 +424,8 @@ module.exports = function(RED) { imap.end(); s = false; setInputRepeatTimeout(); + msg.payload = 0; + done(); return; } @@ -454,6 +463,8 @@ module.exports = function(RED) { imap.end(); s = false; setInputRepeatTimeout(); + msg.payload = results.length; + done(); }; if (node.disposition === "Delete") { imap.addFlags(results, "\Deleted", cleanup); @@ -469,6 +480,7 @@ module.exports = function(RED) { imap.end(); s = false; setInputRepeatTimeout(); + done(err); }); } }); // End of imap->search @@ -478,6 +490,7 @@ module.exports = function(RED) { node.error(e.toString(),e); s = ss = false; imap.end(); + done(e); return; } } @@ -486,6 +499,7 @@ module.exports = function(RED) { node.error(RED._("email.errors.bad_criteria"),msg); s = ss = false; imap.end(); + done(); return; } } @@ -497,11 +511,11 @@ module.exports = function(RED) { // Perform a check of the email inboxes using either POP3 or IMAP - function checkEmail(msg) { + function checkEmail(msg,send,done) { if (node.protocol === "POP3") { - checkPOP3(msg); + checkPOP3(msg,send,done); } else if (node.protocol === "IMAP") { - if (s === false && ss == false) { checkIMAP(msg); } + if (s === false && ss == false) { checkIMAP(msg,send,done); } } } // End of checkEmail @@ -528,11 +542,12 @@ module.exports = function(RED) { }); } - this.on("input", function(msg) { - checkEmail(msg); + node.on("input", function(msg, send, done) { + send = send || function() { node.send.apply(node,arguments) }; + checkEmail(msg,send,done); }); - this.on("close", function() { + node.on("close", function() { if (this.interval_id != null) { clearTimeout(this.interval_id); } diff --git a/social/email/package.json b/social/email/package.json index da850414..8a7a7e29 100644 --- a/social/email/package.json +++ b/social/email/package.json @@ -1,11 +1,11 @@ { "name": "node-red-node-email", - "version": "1.16.0", + "version": "1.17.0", "description": "Node-RED nodes to send and receive simple emails.", "dependencies": { "imap": "^0.8.19", "mailparser": "^3.5.0", - "nodemailer": "^6.7.5", + "nodemailer": "^6.7.7", "smtp-server": "^3.11.0" }, "bundledDependencies": [ From 67611f6bb692ea6355150c2906d86f38db0530d0 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Fri, 22 Jul 2022 10:18:02 +0100 Subject: [PATCH 29/86] Add cbor object packing node and test --- parsers/cbor/70-cbor.html | 44 +++++++++++++ parsers/cbor/70-cbor.js | 38 +++++++++++ parsers/cbor/LICENSE | 14 ++++ parsers/cbor/README.md | 27 ++++++++ parsers/cbor/icons/parser-cbor.png | Bin 0 -> 274 bytes parsers/cbor/package.json | 28 ++++++++ test/parsers/cbor/70-cbor_spec.js | 101 +++++++++++++++++++++++++++++ 7 files changed, 252 insertions(+) create mode 100644 parsers/cbor/70-cbor.html create mode 100644 parsers/cbor/70-cbor.js create mode 100644 parsers/cbor/LICENSE create mode 100644 parsers/cbor/README.md create mode 100644 parsers/cbor/icons/parser-cbor.png create mode 100644 parsers/cbor/package.json create mode 100644 test/parsers/cbor/70-cbor_spec.js diff --git a/parsers/cbor/70-cbor.html b/parsers/cbor/70-cbor.html new file mode 100644 index 00000000..8367e80e --- /dev/null +++ b/parsers/cbor/70-cbor.html @@ -0,0 +1,44 @@ + + + + + + diff --git a/parsers/cbor/70-cbor.js b/parsers/cbor/70-cbor.js new file mode 100644 index 00000000..abed6dbf --- /dev/null +++ b/parsers/cbor/70-cbor.js @@ -0,0 +1,38 @@ + +module.exports = function(RED) { + "use strict"; + var cbor = require('cbor-x'); + + function CborNode(n) { + RED.nodes.createNode(this,n); + this.property = n.property||"payload"; + var node = this; + this.on("input", function(msg) { + var value = RED.util.getMessageProperty(msg,node.property); + if (value !== undefined) { + if (Buffer.isBuffer(value)) { + var l = value.length; + try { + value = cbor.decode(value); + RED.util.setMessageProperty(msg,node.property,value); + node.send(msg); + node.status({text:l +" b->o "+ JSON.stringify(value).length}); + } + catch (e) { + node.error("Bad decode",msg); + node.status({text:"not a cbor buffer"}); + } + } + else { + var le = JSON.stringify(value).length; + value = cbor.encode(value); + RED.util.setMessageProperty(msg,node.property,value); + node.send(msg); + node.status({text:le +" o->b "+ value.length}); + } + } + else { node.warn("No payload found to process"); } + }); + } + RED.nodes.registerType("cbor",CborNode); +} diff --git a/parsers/cbor/LICENSE b/parsers/cbor/LICENSE new file mode 100644 index 00000000..f5b60114 --- /dev/null +++ b/parsers/cbor/LICENSE @@ -0,0 +1,14 @@ +Copyright 2016 JS Foundation and other contributors, https://js.foundation/ +Copyright 2013-2016 IBM Corp. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/parsers/cbor/README.md b/parsers/cbor/README.md new file mode 100644 index 00000000..d0c7fbf9 --- /dev/null +++ b/parsers/cbor/README.md @@ -0,0 +1,27 @@ +node-red-node-cbor +================== + +A Node-RED node to pack and unpack objects to cbor format buffers. + +Install +------- + +Run the following command in your Node-RED user directory - typically `~/.node-red` + + npm install node-red-node-cbor + +Changes +------- + +Version 1.0.0 - move to cbor-x library (more supported and faster). +Usage +----- + +Uses the cbor-x npm to pack and unpack msg.payload objects to cbor format buffers. + +**Note**: this node does not currently encode raw buffer types. +It will automatically try to *decode* any buffer received, and may not cause an error. + +If the input is NOT a buffer it converts it into a msgpack buffer. + +If the input is a msgpack buffer it converts it back to the original type. diff --git a/parsers/cbor/icons/parser-cbor.png b/parsers/cbor/icons/parser-cbor.png new file mode 100644 index 0000000000000000000000000000000000000000..02b5ddc0827853bf3ab620a1f75530fdeb164a43 GIT binary patch literal 274 zcmeAS@N?(olHy`uVBq!ia0vp^B0wz1!3HFCgzU0`6kC$Fy9>jA5L~c#`DCC7XMsm# zF#`j)FbFd;%$g$s6l5>)^mS!_#KbJX#O@!t;~P+DzNd?0h{y4#lh1NB8wj+dd+{B0 ze);%Ei(P~M)87@@j>oziJ7Rlxc_|j3(&(hXT31tjqj=@ z%fGFh{N)qNI%j^S+!ve=n0^&-Y-77{%=tzQo62Orlqq{{WzTKl>{%Cf=ca?-sb7L_ zYpYKys`tn~jCnpGOu{|*+EU{)zQ3kP{KG*bNPSd+y^`=_8P&;onpeP7|t$O Q0(2^ar>mdKI;Vst0Qie#$N&HU literal 0 HcmV?d00001 diff --git a/parsers/cbor/package.json b/parsers/cbor/package.json new file mode 100644 index 00000000..eb0534c2 --- /dev/null +++ b/parsers/cbor/package.json @@ -0,0 +1,28 @@ +{ + "name" : "node-red-node-cbor", + "version" : "1.0.0", + "description" : "A Node-RED node to pack and unpack objects to cbor format", + "dependencies" : { + "cbor-x" : "^1.3.2" + }, + "bundledDependencies": [ + "cbor-x" + ], + "repository" : { + "type":"git", + "url":"https://github.com/node-red/node-red-nodes.git", + "directory": "tree/master/parsers/cbor" + }, + "license": "Apache-2.0", + "keywords": [ "node-red", "cbor" ], + "node-red" : { + "nodes" : { + "cbor": "70-cbor.js" + } + }, + "author": { + "name": "Dave Conway-Jones", + "email": "dceejay@gmail.com", + "url": "http://nodered.org" + } +} diff --git a/test/parsers/cbor/70-cbor_spec.js b/test/parsers/cbor/70-cbor_spec.js new file mode 100644 index 00000000..bcebe91c --- /dev/null +++ b/test/parsers/cbor/70-cbor_spec.js @@ -0,0 +1,101 @@ + +var should = require("should"); +var helper = require("node-red-node-test-helper"); +var testNode = require('../../../parsers/cbor/70-cbor.js'); + +describe('cbor node', function() { + "use strict"; + + beforeEach(function(done) { + helper.startServer(done); + }); + + afterEach(function(done) { + helper.unload().then(function() { + helper.stopServer(done); + }); + }); + + it("should be loaded with correct defaults", function(done) { + var flow = [{"id":"n1", "type":"cbor", "name":"cbor1", "wires":[[]]}]; + helper.load(testNode, flow, function() { + var n1 = helper.getNode("n1"); + n1.should.have.property("name", "cbor1"); + done(); + }); + }); + + var buf; + + it('should pack an object', function(done) { + var flow = [{"id":"n1", "type":"cbor", wires:[["n2"]] }, + {id:"n2", type:"helper"}]; + helper.load(testNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + msg.should.have.a.property("payload"); + msg.payload.should.be.a.Object; + msg.payload.should.have.length(47); + buf = msg.payload; + done(); + }); + n1.emit("input", {payload:{A:1, B:"string", C:true, D:[1,true,"string"], E:{Y:9,Z:"string"}}}); + }); + }); + + it('should unpack a Buffer', function(done) { + var flow = [{"id":"n1", "type":"cbor", wires:[["n2"]] }, + {id:"n2", type:"helper"} ]; + helper.load(testNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + msg.should.have.a.property("payload"); + msg.payload.should.have.a.property("A",1); + msg.payload.should.have.a.property("B",'string'); + msg.payload.should.have.a.property("C",true); + msg.payload.should.have.a.property("D",[1,true,"string"]); + msg.payload.should.have.a.property("E"); + msg.payload.E.should.have.a.property("Y",9); + msg.payload.E.should.have.a.property("Z","string"); + done(); + }); + n1.emit("input", {payload:buf}); + }); + }); + + it('should error if the buffer fails to decode', function(done) { + buf[0] = 0x87; + var flow = [{"id":"n1", "type":"cbor", wires:[["n2"]] }, + {id:"n2", type:"helper"} ]; + helper.load(testNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + done("should not get here if there is an error."); + }); + setTimeout(function() { + done(); + }, 25); + n1.emit("input", {payload:buf}); + }); + }); + + it('ignore msg with no payload', function(done) { + var flow = [{"id":"n1", "type":"cbor", wires:[["n2"]] }, + {id:"n2", type:"helper"} ]; + helper.load(testNode, flow, function() { + var n1 = helper.getNode("n1"); + var n2 = helper.getNode("n2"); + n2.on("input", function(msg) { + done("should not get here with no payload."); + }); + setTimeout(function() { + done(); + }, 25); + n1.emit("input", {topic:1}); + }); + }); + +}); From 7a5c39ce7b92d864c2da7fe4c914e36ceaf57b68 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Fri, 22 Jul 2022 10:18:18 +0100 Subject: [PATCH 30/86] fix snmp node linting --- io/snmp/package.json | 4 ++-- io/snmp/snmp.js | 46 +++++++++++++++++++++++--------------------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/io/snmp/package.json b/io/snmp/package.json index f2023d0f..8bb38f20 100644 --- a/io/snmp/package.json +++ b/io/snmp/package.json @@ -1,9 +1,9 @@ { "name" : "node-red-node-snmp", - "version" : "1.0.1", + "version" : "1.0.2", "description" : "A Node-RED node that gets and sets SNMP oid values. Supports v1, v2c and v3", "dependencies" : { - "net-snmp" : "^3.6.3" + "net-snmp" : "^3.8.2" }, "repository" : { "type":"git", diff --git a/io/snmp/snmp.js b/io/snmp/snmp.js index ca1119db..8c7ca99d 100644 --- a/io/snmp/snmp.js +++ b/io/snmp/snmp.js @@ -141,7 +141,7 @@ module.exports = function (RED) { const IPV6_DOUBLE_COL_PAT = /^\[{0,1}([0-9a-f:]*)::([0-9a-f:]*)(?:\]:(\d+)){0,1}$/g; const ipv4Matcher = IPV4_PAT.exec(ip); let hex = ""; - let port = undefined; + let port; let ipOnly = []; try { @@ -165,7 +165,7 @@ module.exports = function (RED) { const IPV6_PAT = new RegExp(ipv6Pattern); - // IPV6, double colon + // IPV6, double colon const ipv6DoubleColonMatcher = IPV6_DOUBLE_COL_PAT.exec(ip); if (ipv6DoubleColonMatcher && ipv6DoubleColonMatcher.length) { let p1 = ipv6DoubleColonMatcher[1]; @@ -392,24 +392,25 @@ module.exports = function (RED) { node.on("input", function (msg) { const oids = node.oids || msg.oid; const { host, sessionid, user, options } = prepareSnmpOptions(node, msg); + function feedCb(varbinds) { + for (let i = 0; i < varbinds.length; i++) { + if (SNMP.isVarbindError(varbinds[i])) { + node.error(SNMP.varbindError(varbinds[i]), msg); + } else { + response.push({ oid: varbinds[i].oid, value: varbinds[i].value }); + } + } + } if (oids) { msg.oid = oids; let sess = openSession(sessionid, host, user, options); sess.on("error", function (err) { node.error(err, msg); }) - //move response array & feedCb to inside `node.on("input",` to avoid subsequent + //move response array & feedCb to inside `node.on("input",` to avoid subsequent // calls overwriting results from previous operations (each call gets own result/response) const response = []; - function feedCb(varbinds) { - for (let i = 0; i < varbinds.length; i++) { - if (SNMP.isVarbindError(varbinds[i])) { - node.error(SNMP.varbindError(varbinds[i]), msg); - } else { - response.push({ oid: varbinds[i].oid, value: varbinds[i].value }); - } - } - } + sess.subtree(msg.oid, maxRepetitions, feedCb, function (error) { if (error) { node.error(error.toString(), msg); @@ -442,24 +443,25 @@ module.exports = function (RED) { node.on("input", function (msg) { const oids = node.oids || msg.oid; const { host, sessionid, user, options } = prepareSnmpOptions(node, msg); + function feedCb(varbinds) { + for (let i = 0; i < varbinds.length; i++) { + if (SNMP.isVarbindError(varbinds[i])) { + node.error(SNMP.varbindError(varbinds[i]), msg); + } else { + response.push({ oid: varbinds[i].oid, value: varbinds[i].value }); + } + } + } if (oids) { msg.oid = oids; let sess = openSession(sessionid, host, user, options); sess.on("error", function (err) { node.error(err, msg); }) - //move response array & feedCb to inside `node.on("input",` to avoid subsequent + //move response array & feedCb to inside `node.on("input",` to avoid subsequent // calls overwriting results from previous operations (each call gets own result/response) const response = []; - function feedCb(varbinds) { - for (let i = 0; i < varbinds.length; i++) { - if (SNMP.isVarbindError(varbinds[i])) { - node.error(SNMP.varbindError(varbinds[i]), msg); - } else { - response.push({ oid: varbinds[i].oid, value: varbinds[i].value }); - } - } - } + sess.walk(msg.oid, maxRepetitions, feedCb, function (error) { if (error) { node.error(error.toString(), msg); From a7b9380f2503a173ddb33c31b9fd30c7ae17ffb9 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Fri, 22 Jul 2022 10:18:37 +0100 Subject: [PATCH 31/86] fix mysql node error scope --- storage/mysql/68-mysql.js | 2 +- storage/mysql/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/mysql/68-mysql.js b/storage/mysql/68-mysql.js index 2f30910d..87dad1b3 100644 --- a/storage/mysql/68-mysql.js +++ b/storage/mysql/68-mysql.js @@ -180,7 +180,7 @@ module.exports = function(RED) { }); } else { - node.error(RED._("mysql.errors.notconfigured")); + this.error(RED._("mysql.errors.notconfigured")); } } RED.nodes.registerType("mysql",MysqlDBNodeIn); diff --git a/storage/mysql/package.json b/storage/mysql/package.json index aa7d93ac..dabe5c85 100644 --- a/storage/mysql/package.json +++ b/storage/mysql/package.json @@ -1,6 +1,6 @@ { "name": "node-red-node-mysql", - "version": "1.0.2", + "version": "1.0.3", "description": "A Node-RED node to read and write to a MySQL database", "dependencies": { "mysql2": "^2.3.3" From 5bb8b4d968db597156e5df2f7561d677aa8ca2ad Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Wed, 27 Jul 2022 14:08:02 +0100 Subject: [PATCH 32/86] add version/engine support metadata --- parsers/cbor/package.json | 6 +++++- parsers/markdown/package.json | 8 ++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/parsers/cbor/package.json b/parsers/cbor/package.json index eb0534c2..ca9cfafd 100644 --- a/parsers/cbor/package.json +++ b/parsers/cbor/package.json @@ -1,6 +1,6 @@ { "name" : "node-red-node-cbor", - "version" : "1.0.0", + "version" : "1.0.1", "description" : "A Node-RED node to pack and unpack objects to cbor format", "dependencies" : { "cbor-x" : "^1.3.2" @@ -16,6 +16,7 @@ "license": "Apache-2.0", "keywords": [ "node-red", "cbor" ], "node-red" : { + "version": ">=1.0.0", "nodes" : { "cbor": "70-cbor.js" } @@ -24,5 +25,8 @@ "name": "Dave Conway-Jones", "email": "dceejay@gmail.com", "url": "http://nodered.org" + }, + "engines": { + "node": ">=14" } } diff --git a/parsers/markdown/package.json b/parsers/markdown/package.json index 7dd44f2c..c0320a10 100644 --- a/parsers/markdown/package.json +++ b/parsers/markdown/package.json @@ -1,9 +1,9 @@ { "name": "node-red-node-markdown", - "version": "0.3.0", + "version": "0.4.0", "description": "A Node-RED node to convert a markdown string to html.", "dependencies": { - "markdown-it": "^12.3.2" + "markdown-it": "^13.0.1" }, "repository": { "type": "git", @@ -16,6 +16,7 @@ "markdown" ], "node-red": { + "version": ">=1.0.0", "nodes": { "markdown": "70-markdown.js" } @@ -24,5 +25,8 @@ "name": "Dave Conway-Jones", "email": "ceejay@vnet.ibm.com", "url": "http://nodered.org" + }, + "engines": { + "node": ">=14" } } From e5e8a33d804b4a70036873be9ed9789e3c70a354 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Fri, 19 Aug 2022 11:40:53 +0100 Subject: [PATCH 33/86] bump notify and timeswitch deps --- social/notify/package.json | 4 ++-- time/timeswitch/package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/social/notify/package.json b/social/notify/package.json index 5344fb9c..7b332caa 100644 --- a/social/notify/package.json +++ b/social/notify/package.json @@ -1,9 +1,9 @@ { "name": "node-red-node-notify", - "version": "0.2.0", + "version": "0.3.0", "description": "A Node-RED node to send local popup Notify alerts", "dependencies": { - "node-notifier": "^5.4.5" + "node-notifier": "^10.0.1" }, "repository": { "type": "git", diff --git a/time/timeswitch/package.json b/time/timeswitch/package.json index 0f9c9a51..6c86697c 100644 --- a/time/timeswitch/package.json +++ b/time/timeswitch/package.json @@ -1,9 +1,9 @@ { "name" : "node-red-node-timeswitch", - "version" : "0.1.0", + "version" : "0.2.0", "description" : "A Node-RED node to provide a simple timeswitch to schedule daily on/off events.", "dependencies" : { - "spacetime": "^6.12.5", + "spacetime": "^6.16.3", "suncalc": "^1.8.0" }, "repository" : { From d913171f40eb4b87fd5e4964972a66198bdfbb08 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Sat, 20 Aug 2022 18:56:34 +0100 Subject: [PATCH 34/86] Pushbullet - Fix sms missing notifications issue to close #936 --- social/pushbullet/57-pushbullet.js | 13 +++++++++---- social/pushbullet/package.json | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/social/pushbullet/57-pushbullet.js b/social/pushbullet/57-pushbullet.js index 7e6f0ba1..b2068edb 100644 --- a/social/pushbullet/57-pushbullet.js +++ b/social/pushbullet/57-pushbullet.js @@ -221,8 +221,14 @@ module.exports = function(RED) { msg.payload = incoming.iden; } else if (incoming.type === 'sms_changed') { - msg.topic = "SMS: "+ incoming.notifications[0].title; - msg.payload = incoming.notifications[0].body; + if (incoming.notifications && incoming.notifications.length > 0) { + msg.topic = "SMS: "+ incoming.notifications[0].title; + msg.payload = incoming.notifications[0].body; + } + else { + msg.topic = "SMS: "; + msg.payload = ""; + } msg.message = incoming; } else { @@ -244,12 +250,11 @@ module.exports = function(RED) { function PushbulletOut(n) { RED.nodes.createNode(this, n); - var self = this; - this.title = n.title; this.chan = n.chan; this.pushtype = n.pushtype; this.pusher = null; + var self = this; var configNode; diff --git a/social/pushbullet/package.json b/social/pushbullet/package.json index 6054b93d..a7f2f337 100644 --- a/social/pushbullet/package.json +++ b/social/pushbullet/package.json @@ -1,6 +1,6 @@ { "name" : "node-red-node-pushbullet", - "version" : "0.0.17", + "version" : "0.0.19", "description" : "A Node-RED node to send alerts via Pushbullet", "dependencies" : { "pushbullet": "^2.4.0", From ecb2849675b305c571c0c92782de28d9ff4e8662 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Mon, 22 Aug 2022 10:27:49 +0100 Subject: [PATCH 35/86] timeswitch, fix display status to local timezone. to close #944 --- time/timeswitch/package.json | 2 +- time/timeswitch/timeswitch.js | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/time/timeswitch/package.json b/time/timeswitch/package.json index 6c86697c..5bc07d1d 100644 --- a/time/timeswitch/package.json +++ b/time/timeswitch/package.json @@ -1,6 +1,6 @@ { "name" : "node-red-node-timeswitch", - "version" : "0.2.0", + "version" : "0.2.1", "description" : "A Node-RED node to provide a simple timeswitch to schedule daily on/off events.", "dependencies" : { "spacetime": "^6.16.3", diff --git a/time/timeswitch/timeswitch.js b/time/timeswitch/timeswitch.js index 87802a8e..28adb4f6 100644 --- a/time/timeswitch/timeswitch.js +++ b/time/timeswitch/timeswitch.js @@ -51,6 +51,7 @@ module.exports = function(RED) { var today = Math.round((nowMillis - midnightMillis) / 60000) % 1440; var starttime = Number(node.startt); var endtime = Number(node.endt); + var tzOff = (new Date()).getTimezoneOffset(); if ((starttime >= 5000) || (endtime == 5000) || (endtime == 6000)) { var times = SunCalc.getTimes(now, node.lat, node.lon); @@ -109,13 +110,11 @@ module.exports = function(RED) { } if (proceed >= 2) { - var duration = newendtime - today; - if (today > newendtime) { duration += 1440; } - //node.status({fill:"yellow",shape:"dot",text:"on for " + duration + " mins"}); - node.status({fill:"yellow", shape:"dot", text:"on until " + parseInt(newendtime / 60) + ":" + ("0" + newendtime % 60).substr(-2)}); + node.status({fill:"yellow", shape:"dot", text:"on until " + parseInt((newendtime -tzOff) / 60) + ":" + ("0" + (newendtime - tzOff) % 60).substr(-2)}); + } + else { + node.status({fill:"blue", shape:"dot", text:"off until " + parseInt((starttime - tzOff) / 60) + ":" + ("0" + (starttime - tzOff) % 60).substr(-2)}); } - //else { node.status({fill:"blue",shape:"dot",text:"off"}); } - else { node.status({fill:"blue", shape:"dot", text:"off until " + parseInt(starttime / 60) + ":" + ("0" + starttime % 60).substr(-2)}); } var msg = {}; if (node.mytopic) { msg.topic = node.mytopic; } From 29e0bed000c172faf0c13b3a1116930b1931d206 Mon Sep 17 00:00:00 2001 From: "J.D. Mallen" Date: Tue, 23 Aug 2022 09:31:18 -0400 Subject: [PATCH 36/86] Address incorrect timestamps bug in node-red-node-timeswitch; increase readability (#945) * Initial fixes before testing and reformatting * Put sun event offset back in; reformat * Tabs to spaces * Fix "Start+X" OFF time; add more comments * Undo some formatting changes * Add contributors to package.json --- time/timeswitch/package.json | 14 +- time/timeswitch/timeswitch.js | 237 ++++++++++++++++++++++------------ 2 files changed, 169 insertions(+), 82 deletions(-) diff --git a/time/timeswitch/package.json b/time/timeswitch/package.json index 5bc07d1d..b7b81098 100644 --- a/time/timeswitch/package.json +++ b/time/timeswitch/package.json @@ -25,7 +25,19 @@ }, "contributors": [ { - "name": "@pmacostapdi" + "name": "@dceejay" + }, + { + "name": "@pmacostapdi" + }, + { + "name": "@heikokue" + }, + { + "name": "@sammachin" + }, + { + "name": "@jdmallen" } ] } diff --git a/time/timeswitch/timeswitch.js b/time/timeswitch/timeswitch.js index 28adb4f6..3048f378 100644 --- a/time/timeswitch/timeswitch.js +++ b/time/timeswitch/timeswitch.js @@ -1,19 +1,18 @@ - -module.exports = function(RED) { +module.exports = function (RED) { "use strict"; var SunCalc = require('suncalc'); const spacetime = require("spacetime") + const SUNRISE_KEY = "sunrise"; + const SUNSET_KEY = "sunset"; function TimeswitchNode(n) { RED.nodes.createNode(this, n); this.lat = n.lat; this.lon = n.lon; - this.start = n.start || "sunrise"; - this.end = n.end || "sunset"; this.startt = n.starttime; this.endt = n.endtime; - this.duskoff = n.duskoff; - this.dawnoff = n.dawnoff; + this.sunriseOffset = n.dawnoff; + this.sunsetOffset = n.duskoff; this.mytopic = n.mytopic; this.timezone = n.timezone || "UTC"; @@ -24,6 +23,7 @@ module.exports = function(RED) { this.thu = n.thu; this.fri = n.fri; this.sat = n.sat; + this.jan = n.jan; this.feb = n.feb; this.mar = n.mar; @@ -38,117 +38,192 @@ module.exports = function(RED) { this.dec = n.dec; var node = this; - var ison = 0; - var newendtime = 0; - this.on("input", function(msg2) { - if (msg2.payload === "reset") { ison = 0; } + this.on("input", function () { + // current global time + const now = spacetime.now(); + const nowNative = now.toNativeDate(); - var timeOffset = spacetime(Date.now()).goto(this.timezone.toLowerCase()).timezone().current.offset * 60 * 60 * 1000; - var now = new Date(Date.now() + timeOffset); - var nowMillis = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours(), now.getUTCMinutes(), 0); - var midnightMillis = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), 0, 0); - var today = Math.round((nowMillis - midnightMillis) / 60000) % 1440; - var starttime = Number(node.startt); - var endtime = Number(node.endt); - var tzOff = (new Date()).getTimezoneOffset(); + // all sun events for the given lat/long + const sunEvents = SunCalc.getTimes(nowNative, node.lat, node.lon); + let sunriseDateTime = spacetime(sunEvents[SUNRISE_KEY]).nearest("minute"); + let sunsetDateTime = spacetime(sunEvents[SUNSET_KEY]).nearest("minute"); - if ((starttime >= 5000) || (endtime == 5000) || (endtime == 6000)) { - var times = SunCalc.getTimes(now, node.lat, node.lon); - var startMillis = Date.UTC(times[node.start].getUTCFullYear(), times[node.start].getUTCMonth(), times[node.start].getUTCDate(), times[node.start].getUTCHours(), times[node.start].getUTCMinutes()); - var endMillis = Date.UTC(times[node.end].getUTCFullYear(), times[node.end].getUTCMonth(), times[node.end].getUTCDate(), times[node.end].getUTCHours(), times[node.end].getUTCMinutes()); - var dawn = ((startMillis - midnightMillis) / 60000) + Number(node.dawnoff); - var dusk = ((endMillis - midnightMillis) / 60000) + Number(node.duskoff); - if (starttime == 5000) { starttime = dawn; } - if (starttime == 6000) { starttime = dusk; } - if (endtime == 5000) { endtime = dawn; } - if (endtime == 6000) { endtime = dusk; } - if (RED.settings.verbose) { node.log("Dawn " + parseInt(dawn / 60) + ":" + dawn % 60 + " - Dusk " + parseInt(dusk / 60) + ":" + dusk % 60); } + // add optional sun event offset, if specified + sunriseDateTime = sunriseDateTime.add(Number(node.sunriseOffset), "minutes"); + sunsetDateTime = sunsetDateTime.add(Number(node.sunsetOffset), "minutes"); + + // check if sun event has already occurred today + if (now.isAfter(sunriseDateTime)) { + // get tomorrow's sunrise, since it'll be different + sunriseDateTime = spacetime(SunCalc.getTimes(now.add(1, "day").toNativeDate(), node.lat, node.lon)[SUNRISE_KEY]).nearest("minute"); + // add optional sun event offset, if specified (again) + sunriseDateTime = sunriseDateTime.add(Number(node.sunriseOffset), "minutes"); + } + if (now.isAfter(sunsetDateTime)) { + // get tomorrow's sunset, since it'll be different + sunsetDateTime = spacetime(SunCalc.getTimes(now.add(1, "day").toNativeDate(), node.lat, node.lon)[SUNSET_KEY]).nearest("minute"); + // add optional sun event offset, if specified (again) + sunsetDateTime = sunsetDateTime.add(Number(node.sunsetOffset), "minutes"); } - var proceed = 0; - switch (now.getDay()) { - case 0 : { if (node.sun) { proceed++; } break; } - case 1 : { if (node.mon) { proceed++; } break; } - case 2 : { if (node.tue) { proceed++; } break; } - case 3 : { if (node.wed) { proceed++; } break; } - case 4 : { if (node.thu) { proceed++; } break; } - case 5 : { if (node.fri) { proceed++; } break; } - case 6 : { if (node.sat) { proceed++; } break; } + // log sun events + if (RED.settings.verbose) { + node.log(`Sunrise ${sunriseDateTime.format("time")} - Sunset ${sunsetDateTime.format("time")} `); } - if (proceed) { - switch (now.getMonth()) { - case 0 : { if (node.jan) { proceed++; } break; } - case 1 : { if (node.feb) { proceed++; } break; } - case 2 : { if (node.mar) { proceed++; } break; } - case 3 : { if (node.apr) { proceed++; } break; } - case 4 : { if (node.may) { proceed++; } break; } - case 5 : { if (node.jun) { proceed++; } break; } - case 6 : { if (node.jul) { proceed++; } break; } - case 7 : { if (node.aug) { proceed++; } break; } - case 8 : { if (node.sep) { proceed++; } break; } - case 9 : { if (node.oct) { proceed++; } break; } - case 10: { if (node.nov) { proceed++; } break; } - case 11: { if (node.dec) { proceed++; } break; } + // apply selected timezone to selected times (not to sunrise/sunset-- those are based on lat/long) + const currentTimeZone = now.timezone(); + const selectedTimeZone = spacetime(now.epoch, this.timezone.toLowerCase()).timezone(); + + // handler function to convert minute strings (from
+ +
+ + +
+
diff --git a/hardware/intel/mraa-gpio-led.js b/hardware/intel/mraa-gpio-led.js index d809c64d..d9a4ce08 100644 --- a/hardware/intel/mraa-gpio-led.js +++ b/hardware/intel/mraa-gpio-led.js @@ -3,71 +3,75 @@ module.exports = function(RED) { function LEDNode(n) { RED.nodes.createNode(this, n); this.pin = Number(n.pin); - this.led0 = new m.Led(0); /*user-led1-green*/ - this.led1 = new m.Led(1); /*user-led1-red*/ - this.led2 = new m.Led(2); /*user-led2-green*/ - this.led3 = new m.Led(3); /*user-led2-red*/ + this.color = Number(n.color); + + if (this.pin == 0) { + this.user1_green = new m.Led(0); /*user-led1-green*/ + this.user1_red = new m.Led(1); /*user-led1-red*/ + } if(this.pin == 1) { + this.user2_green = new m.Led(2); /*user-led2-green*/ + this.user2_red = new m.Led(3); /*user-led2-red*/ + } + + function set_led_green(led_green, led_red) { + led_green.setBrightness(1); + led_red.setBrightness(0); + } + + function set_led_red(led_green, led_red) { + led_green.setBrightness(0); + led_red.setBrightness(1); + } + + function set_led_orange(led_green, led_red) { + led_green.setBrightness(1); + led_red.setBrightness(1); + } + + function turn_off_led(led_green, led_red) { + led_green.setBrightness(0); + led_red.setBrightness(0); + } + this.on("input", function(msg) { + if (this.pin == 0) { + this.led_green = this.user1_green; + this.led_red = this.user1_red; + } + else if (this.pin == 1) { + this.led_green = this.user2_green; + this.led_red = this.user2_red; + } + if (msg.payload == "1") { - switch(this.pin) - { - case 0: /*User0 Led Green*/ - this.led0.setBrightness(1); + switch(this.color) { + case 0: + set_led_green(this.led_green, this.led_red); break; - case 1: /*User0 Led Red*/ - this.led1.setBrightness(1); + case 1: + set_led_red(this.led_green, this.led_red); break; - case 2: /*User0 Orange*/ - this.led0.setBrightness(1); - this.led1.setBrightness(1); - break; - case 3: /*User1 Led Green*/ - this.led2.setBrightness(1); - break; - case 4: /*User1 Led Red*/ - this.led3.setBrightness(1); - break; - case 5: /*User1 Orange*/ - this.led2.setBrightness(1); - this.led3.setBrightness(1); + case 2: + set_led_orange(this.led_green, this.led_red); break; default: + console.log("unexpected"); break; } } else { - switch(this.pin) - { - case 0: /*User1 Led Green*/ - this.led0.setBrightness(0); - break; - case 1: /*User1 Led Red*/ - this.led1.setBrightness(0); - break; - case 2: /*User1 Orange*/ - this.led0.setBrightness(0); - this.led1.setBrightness(0); - break; - case 3: /*User2 Led Green*/ - this.led2.setBrightness(0); - break; - case 4: /*User2 Led Red*/ - this.led3.setBrightness(0); - break; - case 5: /*User2 Orange*/ - this.led2.setBrightness(0); - this.led3.setBrightness(0); - break; - default: - break; - } + turn_off_led(this.led_green, this.led_red); } }); + this.on('close', function() { - this.led0.close(); - this.led1.close(); - this.led2.close(); - this.led3.close(); + if (this.pin == 0) { + this.user1_green.close(); + this.user1_red.close(); + } if(this.pin == 1) { + this.user2_green.close(); + this.user2_red.close(); + } }); } RED.nodes.registerType("mraa-gpio-led", LEDNode); From c15fa79e9535d029d206dfb76474d56bf979504b Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Wed, 26 Oct 2022 15:19:57 +0100 Subject: [PATCH 53/86] Bump for PR --- hardware/intel/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardware/intel/package.json b/hardware/intel/package.json index dcd692b6..6238514b 100644 --- a/hardware/intel/package.json +++ b/hardware/intel/package.json @@ -1,6 +1,6 @@ { "name" : "node-red-node-intel-gpio", - "version" : "0.2.1", + "version" : "0.3.0", "description" : "A Node-RED node to talk to an Intel Galileo, Edison or Siemens IOT2050 board using mraa", "dependencies" : { }, From 4c9afda6dd67854e0da975f46e56588b9f1e1bd7 Mon Sep 17 00:00:00 2001 From: kaffetorsk <93540369+kaffetorsk@users.noreply.github.com> Date: Wed, 26 Oct 2022 16:22:48 +0200 Subject: [PATCH 54/86] node-red-daemon append arguments (#939) * Allow msg.start to append arguments * Update description * Update README.md --- utility/daemon/README.md | 2 +- utility/daemon/daemon.js | 36 ++++++++++++++++-------- utility/daemon/locales/en-US/daemon.html | 2 +- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/utility/daemon/README.md b/utility/daemon/README.md index 57577643..95f41c16 100644 --- a/utility/daemon/README.md +++ b/utility/daemon/README.md @@ -34,7 +34,7 @@ to restart the command automatically. Setting `msg.kill` to a signal name (e.g. SIGINT, SIGHUP) will stop the process - but if the restart flag is set it will then auto restart. -Sending `msg.start` will also re-start the process. +Sending `msg.start` will also re-start the process. Additional arguments can be specified in `msg.args`. **Note:** Some applications will automatically buffer lines of output. It is advisable to turn off this behaviour. For example, if running a Python app, the `-u` parameter will stop the output being buffered. diff --git a/utility/daemon/daemon.js b/utility/daemon/daemon.js index bbee3332..928f8143 100644 --- a/utility/daemon/daemon.js +++ b/utility/daemon/daemon.js @@ -15,16 +15,21 @@ module.exports = function(RED) { this.closer = n.closer || "SIGKILL"; this.autorun = true; if (n.autorun === false) { this.autorun = false; } - if (this.args.match(/^\[.*\]$/)) { - try { this.args = JSON.parse(this.args); } - catch(e) { - node.warn(RED._("daemon.errors.badparams")) - } - } - else { this.args = this.args.match(/("[^"]*")|[^ ]+/g); } + this.args = parseArgs(this.args); var node = this; var lastmsg = {}; + function parseArgs(args) { + if (args.match(/^\[.*\]$/)) { + try { args = JSON.parse(args); } + catch(e) { + node.warn(RED._("daemon.errors.badparams")) + } + } + else { args = args.match(/("[^"]*")|[^ ]+/g); } + return args; + } + function inputlistener(msg) { if (msg != null) { if (msg.hasOwnProperty("kill") && node.running) { @@ -32,7 +37,11 @@ module.exports = function(RED) { node.child.kill(msg.kill.toUpperCase()); } else if (msg.hasOwnProperty("start") && !node.running) { - runit(); + let args = ""; + if (msg.hasOwnProperty("args") && msg.args.length > 0) { + args = parseArgs(msg.args.trim()); + } + runit(args); } else { if (!Buffer.isBuffer(msg.payload)) { @@ -48,15 +57,20 @@ module.exports = function(RED) { } } - function runit() { + function runit(appendArgs) { var line = ""; if (!node.cmd || (typeof node.cmd !== "string") || (node.cmd.length < 1)) { node.status({fill:"grey",shape:"ring",text:RED._("daemon.status.nocommand")}); return; } + let args = node.args; + if (appendArgs !== undefined && appendArgs.length > 0) { + args = args.concat(appendArgs); + } + try { - node.child = spawn(node.cmd, node.args); - node.debug(node.cmd+" "+JSON.stringify(node.args)); + node.child = spawn(node.cmd, args); + node.debug(node.cmd+" "+JSON.stringify(args)); node.status({fill:"green",shape:"dot",text:RED._("daemon.status.running")}); node.running = true; diff --git a/utility/daemon/locales/en-US/daemon.html b/utility/daemon/locales/en-US/daemon.html index fd1ec70b..06d750d7 100644 --- a/utility/daemon/locales/en-US/daemon.html +++ b/utility/daemon/locales/en-US/daemon.html @@ -5,7 +5,7 @@

Parameters can be space separated, space separated with quotes, or a javascript array. For example `aa bb` or `"cc dd"` or `["aa","bb cc""]`.

If the called program stops (i.e. a return code is produced), this node can attempt to restart the command.

Setting msg.kill to a signal name (e.g. SIGINT, SIGHUP) will stop the process - but if the - restart flag is set it will then auto restart. Sending msg.start will also re-start the process.

+ restart flag is set it will then auto restart. Sending msg.start will also re-start the process. Additional arguments can be specified in msg.args.

Note: Some applications will automatically buffer lines of output. It is advisable to turn off this behaviour. For example, if running a Python app, the -u parameter will stop the output being buffered.

From 93614e09ee41895890dc0df077363fb47a47b662 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Wed, 26 Oct 2022 15:23:57 +0100 Subject: [PATCH 55/86] bump daemon for PR to add msg.args --- utility/daemon/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utility/daemon/package.json b/utility/daemon/package.json index b0e82fd1..1d353087 100644 --- a/utility/daemon/package.json +++ b/utility/daemon/package.json @@ -1,6 +1,6 @@ { "name" : "node-red-node-daemon", - "version" : "0.4.0", + "version" : "0.5.0", "description" : "A Node-RED node that runs and monitors a long running system command.", "dependencies" : { }, From 4c0ecc70bcd7ba9f4c268c5fd7a6d117ef5278c8 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Sat, 29 Oct 2022 16:51:14 +0100 Subject: [PATCH 56/86] add smtp keywords --- social/email/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/social/email/package.json b/social/email/package.json index e7e1afe2..e17af937 100644 --- a/social/email/package.json +++ b/social/email/package.json @@ -26,6 +26,8 @@ "gmail", "imap", "pop", + "smtp", + "smtp-server", "mta" ], "node-red": { From c3a5716fe76c24e349b59a800f79de39b87998a3 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Tue, 8 Nov 2022 13:52:37 +0000 Subject: [PATCH 57/86] Let email node be able to send blank topic and let msg.description be " also to close #959 --- social/email/61-email.js | 6 ++++-- social/email/locales/de/61-email.json | 2 +- social/email/locales/en-US/61-email.json | 4 ++-- social/email/locales/ja/61-email.json | 2 +- social/email/package.json | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/social/email/61-email.js b/social/email/61-email.js index 41918cff..fc717d25 100644 --- a/social/email/61-email.js +++ b/social/email/61-email.js @@ -98,7 +98,8 @@ module.exports = function(RED) { sendopts.headers = msg.headers; sendopts.priority = msg.priority; } - sendopts.subject = msg.topic || msg.title || "Message from Node-RED"; // subject line + if (msg.hasOwnProperty("topic") && msg.topic === '') { sendopts.subject = ""; } + else { sendopts.subject = msg.topic || msg.title || "Message from Node-RED"; } // subject line if (msg.hasOwnProperty("header") && msg.header.hasOwnProperty("message-id")) { sendopts.inReplyTo = msg.header["message-id"]; sendopts.subject = "Re: " + sendopts.subject; @@ -119,7 +120,8 @@ module.exports = function(RED) { sendopts.attachments[0].contentType = msg.headers["content-type"]; } // Create some body text.. - sendopts.text = RED._("email.default-message",{filename:fname, description:(msg.description||"")}); + if (msg.hasOwnProperty("description")) { sendopts.text = msg.description; } + else { sendopts.text = RED._("email.default-message",{filename:fname}); } } else { var payload = RED.util.ensureString(msg.payload); diff --git a/social/email/locales/de/61-email.json b/social/email/locales/de/61-email.json index 545f1aa2..704dfb9e 100644 --- a/social/email/locales/de/61-email.json +++ b/social/email/locales/de/61-email.json @@ -36,7 +36,7 @@ "always": "immer", "rejectUnauthorised": "Überprüfen sie, ob das serverzertifikat gültig ist" }, - "default-message": "__description__\n\nDatei von Node-RED ist angehängt: __filename__", + "default-message": "\nDatei von Node-RED ist angehängt: __filename__", "tip": { "cred": "Hinweis: Berechtigungen von globaler emailkeys.js-Datei kopiert", "recent": "Tipp: Es wird nur die letzte E-Mail abgerufen", diff --git a/social/email/locales/en-US/61-email.json b/social/email/locales/en-US/61-email.json index 67567904..73f25cba 100644 --- a/social/email/locales/en-US/61-email.json +++ b/social/email/locales/en-US/61-email.json @@ -42,10 +42,10 @@ "keyFile":"Private key", "users": "Users", "auth": "Authenticate users", - "addButton": "Add", + "addButton": "Add", "expert": "Expert" }, - "default-message": "__description__\n\nFile from Node-RED is attached: __filename__", + "default-message": "\nFile from Node-RED is attached: __filename__", "tip": { "cred": "Note: Copied credentials from global emailkeys.js file.", "recent": "Tip: Only retrieves the single most recent email.", diff --git a/social/email/locales/ja/61-email.json b/social/email/locales/ja/61-email.json index c4dbc1c4..82c7bcc7 100644 --- a/social/email/locales/ja/61-email.json +++ b/social/email/locales/ja/61-email.json @@ -36,7 +36,7 @@ "always": "常時", "rejectUnauthorised": "チェックサーバ証明書は有効です" }, - "default-message": "__description__\n\nNode-REDからファイルが添付されました: __filename__", + "default-message": "\nNode-REDからファイルが添付されました: __filename__", "tip": { "cred": "注釈: emailkeys.jsファイルから認証情報をコピーしました。", "recent": "注釈: 最新のメールを1件のみ取得します。", diff --git a/social/email/package.json b/social/email/package.json index e17af937..f3c23428 100644 --- a/social/email/package.json +++ b/social/email/package.json @@ -1,6 +1,6 @@ { "name": "node-red-node-email", - "version": "1.18.1", + "version": "1.18.2", "description": "Node-RED nodes to send and receive simple emails.", "dependencies": { "imap": "^0.8.19", From 074ca44b5f881160c3ac347115b7a52f20453da4 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Wed, 16 Nov 2022 12:02:30 +0000 Subject: [PATCH 58/86] Add specific daemons node stdin error handler to address issue #960 --- utility/daemon/daemon.js | 10 ++++++++-- utility/daemon/locales/en-US/daemon.json | 3 ++- utility/daemon/package.json | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/utility/daemon/daemon.js b/utility/daemon/daemon.js index 928f8143..d3ab3817 100644 --- a/utility/daemon/daemon.js +++ b/utility/daemon/daemon.js @@ -50,9 +50,9 @@ module.exports = function(RED) { if (node.cr === true) { msg.payload += "\n"; } } node.debug("inp: "+msg.payload); + lastmsg = msg; if (node.child !== null && node.running) { node.child.stdin.write(msg.payload); } else { node.warn(RED._("daemon.errors.notrunning")); } - lastmsg = msg; } } } @@ -65,7 +65,7 @@ module.exports = function(RED) { } let args = node.args; if (appendArgs !== undefined && appendArgs.length > 0) { - args = args.concat(appendArgs); + args = args.concat(appendArgs); } try { @@ -120,6 +120,12 @@ module.exports = function(RED) { else { node.log('error: ' + err); } node.status({fill:"red",shape:"ring",text:RED._("daemon.status.error")}); }); + + node.child.stdin.on('error', function (err) { + if (err.errno === "EPIPE") { node.error(RED._("daemon.errors.pipeclosed"),lastmsg); } + else { node.log('error: ' + err); } + node.status({fill:"red",shape:"ring",text:RED._("daemon.status.error")}); + }); } catch(e) { if (e.errno === "ENOENT") { node.warn(RED._("daemon.errors.notfound")); } diff --git a/utility/daemon/locales/en-US/daemon.json b/utility/daemon/locales/en-US/daemon.json index 20263c3b..52ef4091 100644 --- a/utility/daemon/locales/en-US/daemon.json +++ b/utility/daemon/locales/en-US/daemon.json @@ -37,7 +37,8 @@ "notrunning": "Command not running", "notfound": "Command not found", "notexecutable": "Command not executable", - "restarting": "Restarting" + "restarting": "Restarting", + "pipeclosed": "Process closed" } } } \ No newline at end of file diff --git a/utility/daemon/package.json b/utility/daemon/package.json index 1d353087..f6b7272b 100644 --- a/utility/daemon/package.json +++ b/utility/daemon/package.json @@ -1,6 +1,6 @@ { "name" : "node-red-node-daemon", - "version" : "0.5.0", + "version" : "0.5.1", "description" : "A Node-RED node that runs and monitors a long running system command.", "dependencies" : { }, From 2da42c949591bd2a6eecd58f707c47159a709811 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Mon, 28 Nov 2022 11:50:08 +0000 Subject: [PATCH 59/86] Let sensehat ignore colour sensor warning to close #958 --- hardware/sensehat/package.json | 2 +- hardware/sensehat/sensehat.js | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/hardware/sensehat/package.json b/hardware/sensehat/package.json index 20947ada..c9845b99 100644 --- a/hardware/sensehat/package.json +++ b/hardware/sensehat/package.json @@ -1,6 +1,6 @@ { "name" : "node-red-node-pi-sense-hat", - "version" : "0.1.3", + "version" : "0.1.4", "description" : "A Node-RED node to interact with a Raspberry Pi Sense HAT", "repository" : { "type":"git", diff --git a/hardware/sensehat/sensehat.js b/hardware/sensehat/sensehat.js index 6172c752..f41d4360 100644 --- a/hardware/sensehat/sensehat.js +++ b/hardware/sensehat/sensehat.js @@ -107,7 +107,13 @@ module.exports = function(RED) { // Any data on stderr means a bad thing has happened. // Best to kill it and let it reconnect. if (RED.settings.verbose) { RED.log.error("err: "+data+" :"); } - hat.kill('SIGKILL'); + if (data.indexOf("WARNING") === 0) { + if (data.indexOf("sensor not present") !== -1) { return; } + else { RED.log.warn(data); } + } + else { + hat.kill('SIGKILL'); + } }); hat.stderr.on('error', function(err) { }); hat.stdin.on('error', function(err) { }); From 2b50bf38ec7b7aec6dd5b0870abf376677b02928 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Wed, 14 Dec 2022 11:45:33 +0000 Subject: [PATCH 60/86] Fix tail to update status and retry if file goes away and comes back --- storage/tail/28-tail.js | 4 ++++ storage/tail/package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/storage/tail/28-tail.js b/storage/tail/28-tail.js index 0f741604..4bdb0fc7 100644 --- a/storage/tail/28-tail.js +++ b/storage/tail/28-tail.js @@ -16,6 +16,7 @@ module.exports = function(RED) { var fileTail = function() { if (fs.existsSync(node.filename)) { + node.status({ }); if (node.filetype === "text") { node.tail = new Tail(node.filename,{separator:node.split, flushAtEOF:true}); } @@ -40,6 +41,7 @@ module.exports = function(RED) { node.tail.on("error", function(err) { node.status({ fill: "red",shape:"ring", text: "node-red:common.status.error" }); node.error(err.toString()); + if (err.code ==="ENOENT") { scheduleRestart(); } }); } else { @@ -51,6 +53,8 @@ module.exports = function(RED) { var scheduleRestart = function() { node.tout = setTimeout(function() { node.tout = null; + if (node.tail) { node.tail.unwatch(); } + delete node.tail; fileTail(); }, 10000); }; diff --git a/storage/tail/package.json b/storage/tail/package.json index d95ade61..a6d542cd 100644 --- a/storage/tail/package.json +++ b/storage/tail/package.json @@ -1,6 +1,6 @@ { "name": "node-red-node-tail", - "version": "0.3.2", + "version": "0.4.0", "description": "A node to tail files for Node-RED", "dependencies": { "tail": "^2.2.4" From b9053cf962eb877b360f8d1ee919687e2a016135 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Thu, 22 Dec 2022 11:38:00 +0000 Subject: [PATCH 61/86] email - better cleanup of deleted messages ? --- social/email/61-email.js | 4 ++-- social/email/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/social/email/61-email.js b/social/email/61-email.js index fc717d25..b7ef9f77 100644 --- a/social/email/61-email.js +++ b/social/email/61-email.js @@ -469,9 +469,9 @@ module.exports = function(RED) { done(); }; if (node.disposition === "Delete") { - imap.addFlags(results, "\Deleted", cleanup); + imap.addFlags(results, '\\Deleted', imap.expunge(cleanup) ); } else if (node.disposition === "Read") { - imap.addFlags(results, "\Seen", cleanup); + imap.addFlags(results, '\\Seen', cleanup); } else { cleanup(); } diff --git a/social/email/package.json b/social/email/package.json index f3c23428..c00d07b2 100644 --- a/social/email/package.json +++ b/social/email/package.json @@ -1,6 +1,6 @@ { "name": "node-red-node-email", - "version": "1.18.2", + "version": "1.18.3", "description": "Node-RED nodes to send and receive simple emails.", "dependencies": { "imap": "^0.8.19", From bb0a6fd272c44233200440fb04c3146c656aa45d Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Thu, 22 Dec 2022 11:39:36 +0000 Subject: [PATCH 62/86] bump email packages --- social/email/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/social/email/package.json b/social/email/package.json index c00d07b2..06524b1a 100644 --- a/social/email/package.json +++ b/social/email/package.json @@ -4,7 +4,7 @@ "description": "Node-RED nodes to send and receive simple emails.", "dependencies": { "imap": "^0.8.19", - "mailparser": "^3.5.0", + "mailparser": "^3.6.2", "nodemailer": "^6.8.0", "smtp-server": "^3.11.0" }, From b046740e7f87dcbb3ca8ba8e00d84d88f5069a4e Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Thu, 22 Dec 2022 11:45:00 +0000 Subject: [PATCH 63/86] Update package.json --- social/email/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/social/email/package.json b/social/email/package.json index 06524b1a..d1dd34a8 100644 --- a/social/email/package.json +++ b/social/email/package.json @@ -31,6 +31,7 @@ "mta" ], "node-red": { + "version": ">=1.0.0", "nodes": { "email": "61-email.js" } From 9bae313b722147b00480ce2e26ed0aab357b836b Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Sat, 24 Dec 2022 14:03:49 +0000 Subject: [PATCH 64/86] revert mailparser to fix node12 (for now) --- social/email/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/social/email/package.json b/social/email/package.json index d1dd34a8..2e362d1a 100644 --- a/social/email/package.json +++ b/social/email/package.json @@ -1,10 +1,10 @@ { "name": "node-red-node-email", - "version": "1.18.3", + "version": "1.18.4", "description": "Node-RED nodes to send and receive simple emails.", "dependencies": { "imap": "^0.8.19", - "mailparser": "^3.6.2", + "mailparser": "~3.5.0", "nodemailer": "^6.8.0", "smtp-server": "^3.11.0" }, From 8553dc30f2d00828c80128e3f42cace2150f9ad0 Mon Sep 17 00:00:00 2001 From: Dave Conway-Jones Date: Fri, 13 Jan 2023 11:42:06 +0000 Subject: [PATCH 65/86] remove dubious use of rtl direction to close #970 --- io/snmp/package.json | 2 +- io/snmp/snmp.html | 36 ++++++++++++++++++------------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/io/snmp/package.json b/io/snmp/package.json index eb783a50..3df8a7cf 100644 --- a/io/snmp/package.json +++ b/io/snmp/package.json @@ -1,6 +1,6 @@ { "name" : "node-red-node-snmp", - "version" : "1.0.5", + "version" : "1.0.6", "description" : "A Node-RED node that gets and sets SNMP oid values. Supports v1, v2c and v3", "dependencies" : { "net-snmp" : "^3.8.2" diff --git a/io/snmp/snmp.html b/io/snmp/snmp.html index f952e331..3e3c029b 100644 --- a/io/snmp/snmp.html +++ b/io/snmp/snmp.html @@ -41,7 +41,7 @@ $(".form-row-snmpv3-auth").toggleClass("hidden", true); $(".form-row-snmpv3-priv").toggleClass("hidden", true); break; - } + } } }); $("#node-input-version").val(node.version); @@ -65,7 +65,7 @@ Timeout -  S +  S
@@ -106,7 +106,7 @@
- +
@@ -177,10 +177,10 @@ - + Timeout -  S +  S
@@ -221,7 +221,7 @@
- +