diff --git a/amtmanager.js b/amtmanager.js index 1affd9e5..8bbf2f59 100644 --- a/amtmanager.js +++ b/amtmanager.js @@ -820,7 +820,7 @@ module.exports.CreateAmtManager = function (parent) { else if (response.Body.OSPowerSavingState == 3) { meshPowerState = 2; } // Modern standby (We are going to call this S1); // Set OS power state - if (meshPowerState >= 0) { parent.SetConnectivityState(dev.meshid, dev.nodeid, Date.now(), 4, meshPowerState); } + if (meshPowerState >= 0) { parent.SetConnectivityState(dev.meshid, dev.nodeid, Date.now(), 4, meshPowerState, null, { name: dev.name }); } }); } else { // Convert the power state @@ -830,7 +830,7 @@ module.exports.CreateAmtManager = function (parent) { if (powerstate < powerConversionTable.length) { meshPowerState = powerConversionTable[powerstate]; } else { powerstate = 6; } // Set power state - if (meshPowerState >= 0) { parent.SetConnectivityState(dev.meshid, dev.nodeid, Date.now(), 4, meshPowerState); } + if (meshPowerState >= 0) { parent.SetConnectivityState(dev.meshid, dev.nodeid, Date.now(), 4, meshPowerState, null, { name: dev.name }); } } }); } diff --git a/amtprovisioningserver.js b/amtprovisioningserver.js index 8cbcb965..741ea88e 100644 --- a/amtprovisioningserver.js +++ b/amtprovisioningserver.js @@ -263,7 +263,7 @@ module.exports.CreateAmtProvisioningServer = function (parent, config) { // Setup TLS and commit. attemptTlsSync(dev, function (dev) { dev.consoleMsg('Intel AMT ACM activation completed.'); - parent.SetConnectivityState(dev.meshid, dev.nodeid, Date.now(), 4, 7); // Report power state as "present" (7). + parent.SetConnectivityState(dev.meshid, dev.nodeid, Date.now(), 4, 7, null, { name: dev.name }); // Report power state as "present" (7). if (obj.parent.amtManager != null) { obj.parent.amtManager.startAmtManagement(dev.nodeid, 3, dev.aquired.host); } // Request that Intel AMT manager take a look at this device. destroyDevice(dev); // We are done, clean up. }); diff --git a/amtscanner.js b/amtscanner.js index c7e34d04..9742f802 100644 --- a/amtscanner.js +++ b/amtscanner.js @@ -178,7 +178,7 @@ module.exports.CreateAmtScanner = function (parent) { if ((scaninfo.state == 1) && (delta >= PeriodicScanTimeout)) { // More than 2 minutes without a response, mark the node as unknown state scaninfo.state = 0; - obj.parent.ClearConnectivityState(scaninfo.nodeinfo.meshid, scaninfo.nodeinfo._id, 4); // Clear connectivity state + obj.parent.ClearConnectivityState(scaninfo.nodeinfo.meshid, scaninfo.nodeinfo._id, 4, null, { name: doc.name }); // Clear connectivity state if (obj.parent.amtManager != null) { obj.parent.amtManager.stopAmtManagement(scaninfo.nodeinfo._id, 3, scaninfo.nodeinfo.host); } } else if ((scaninfo.tcp == null) && ((scaninfo.state == 0) || isNaN(delta) || (delta > PeriodicScanTime))) { // More than 30 seconds without a response, try TCP detection @@ -188,7 +188,7 @@ module.exports.CreateAmtScanner = function (parent) { tag.lastpong = Date.now(); if (tag.state == 0) { tag.state = 1; - obj.parent.SetConnectivityState(tag.nodeinfo.meshid, tag.nodeinfo._id, tag.lastpong, 4, 7); // Report power state as "present" (7). + obj.parent.SetConnectivityState(tag.nodeinfo.meshid, tag.nodeinfo._id, tag.lastpong, 4, 7, null, { name: doc.name }); // Report power state as "present" (7). if (version != null) { obj.changeAmtState(tag.nodeinfo._id, version, 2, tag.nodeinfo.intelamt.tls); } if (obj.parent.amtManager != null) { obj.parent.amtManager.startAmtManagement(tag.nodeinfo._id, 3, tag.nodeinfo.host); } } @@ -299,7 +299,7 @@ module.exports.CreateAmtScanner = function (parent) { scaninfo.nodeinfo.intelamt.ver = majorVersion + '.' + minorVersion; scaninfo.nodeinfo.intelamt.state = provisioningState; } - obj.parent.SetConnectivityState(scaninfo.nodeinfo.meshid, scaninfo.nodeinfo._id, scaninfo.lastpong, 4, 7); // Report power state as "present" (7). + obj.parent.SetConnectivityState(scaninfo.nodeinfo.meshid, scaninfo.nodeinfo._id, scaninfo.lastpong, 4, 7, null, { name: scaninfo.nodeinfo.name }); // Report power state as "present" (7). obj.changeAmtState(scaninfo.nodeinfo._id, scaninfo.nodeinfo.intelamt.ver, provisioningState, scaninfo.nodeinfo.intelamt.tls); if (obj.parent.amtManager != null) { obj.parent.amtManager.startAmtManagement(scaninfo.nodeinfo._id, 3, scaninfo.nodeinfo.host); } } diff --git a/meshagent.js b/meshagent.js index 27d8f06f..d5270d7c 100644 --- a/meshagent.js +++ b/meshagent.js @@ -64,7 +64,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { // Remove this agent from the webserver list if (parent.wsagents[obj.dbNodeKey] == obj) { delete parent.wsagents[obj.dbNodeKey]; - parent.parent.ClearConnectivityState(obj.dbMeshKey, obj.dbNodeKey, 1, null, { remoteaddrport: obj.remoteaddrport }); + parent.parent.ClearConnectivityState(obj.dbMeshKey, obj.dbNodeKey, 1, null, { remoteaddrport: obj.remoteaddrport, name: obj.name }); } // Remove this agent from the list of agents with bad web certificates @@ -119,17 +119,18 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { if (obj.pongtimer) { clearInterval(obj.pongtimer); delete obj.pongtimer; } // Perform aggressive cleanup - if (obj.nonce) { delete obj.nonce; } - if (obj.nodeid) { delete obj.nodeid; } - if (obj.unauth) { delete obj.unauth; } - if (obj.remoteaddr) { delete obj.remoteaddr; } - if (obj.remoteaddrport) { delete obj.remoteaddrport; } - if (obj.meshid) { delete obj.meshid; } - if (obj.dbNodeKey) { delete obj.dbNodeKey; } - if (obj.dbMeshKey) { delete obj.dbMeshKey; } - if (obj.connectTime) { delete obj.connectTime; } - if (obj.agentInfo) { delete obj.agentInfo; } - if (obj.agentExeInfo) { delete obj.agentExeInfo; } + delete obj.name; + delete obj.nonce; + delete obj.nodeid; + delete obj.unauth; + delete obj.remoteaddr; + delete obj.remoteaddrport; + delete obj.meshid; + delete obj.dbNodeKey; + delete obj.dbMeshKey; + delete obj.connectTime; + delete obj.agentInfo; + delete obj.agentExeInfo; ws.removeAllListeners(['message', 'close', 'error']); }; @@ -752,6 +753,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { return; } else { device = nodes[0]; + obj.name = device.name; // This device exists, meshid given by the device must be ignored, use the server side one. if ((device.meshid != null) && (device.meshid != obj.dbMeshKey)) { @@ -888,7 +890,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) { dupAgent.close(3); } else { // Indicate the agent is connected - parent.parent.SetConnectivityState(obj.dbMeshKey, obj.dbNodeKey, obj.connectTime, 1, 1, null, { remoteaddrport: obj.remoteaddrport }); + parent.parent.SetConnectivityState(obj.dbMeshKey, obj.dbNodeKey, obj.connectTime, 1, 1, null, { remoteaddrport: obj.remoteaddrport, name: device.name }); } // We are done, ready to communicate with this agent diff --git a/meshcentral.js b/meshcentral.js index 7f57557b..bcc71279 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -2081,6 +2081,46 @@ function CreateMeshCentralServer(config, args) { } }; + // See if we need to notifiy any user of device state change + obj.NotifyUserOfDeviceStateChange = function (meshid, nodeid, connectTime, connectType, powerState, serverid, stateSet, extraInfo) { + // Check if there is a email server for this domain + const meshSplit = meshid.split('/'); + if (meshSplit.length != 3) return; + const domainId = meshSplit[1]; + const mailserver = obj.config.domains[domainId].mailserver;; + if (mailserver == null) return; + + // Get the device group for this device + const mesh = obj.webserver.meshes[meshid]; + if ((mesh == null) || (mesh.links == null)) return; + + // Check if any user needs email notification + for (var i in mesh.links) { + if (i.startsWith('user/')) { + const user = obj.webserver.users[i]; + if ((user != null) && (user.email != null) && (user.emailVerified == true)) { + const meshLinks = user.links[meshid]; + if ((meshLinks != null) && (meshLinks.notify != null) && ((meshLinks.notify & 48) != 0)) { + if (stateSet == true) { + if ((meshLinks.notify & 16) != 0) { + mailserver.notifyDeviceConnect(user, meshid, nodeid, connectTime, connectType, powerState, serverid, extraInfo); + } else { + mailserver.cancelNotifyDeviceDisconnect(user, meshid, nodeid, connectTime, connectType, powerState, serverid, extraInfo); + } + } + else if (stateSet == false) { + if ((meshLinks.notify & 32) != 0) { + mailserver.notifyDeviceDisconnect(user, meshid, nodeid, connectTime, connectType, powerState, serverid, extraInfo); + } else { + mailserver.cancelNotifyDeviceConnect(user, meshid, nodeid, connectTime, connectType, powerState, serverid, extraInfo); + } + } + } + } + } + } + } + // Set the connectivity state of a node and setup the server so that messages can be routed correctly. // meshId: mesh identifier of format mesh/domain/meshidhex // nodeId: node identifier of format node/domain/nodeidhex @@ -2091,7 +2131,7 @@ function CreateMeshCentralServer(config, args) { //var powerStateStrings = ['Unknown', 'Powered', 'Sleep', 'Sleep', 'Deep Sleep', 'Hibernating', 'Soft-Off', 'Present']; obj.SetConnectivityState = function (meshid, nodeid, connectTime, connectType, powerState, serverid, extraInfo) { //console.log('SetConnectivity for ' + nodeid.substring(0, 16) + ', Type: ' + connectTypeStrings[connectType] + ', Power: ' + powerStateStrings[powerState] + (serverid == null ? ('') : (', ServerId: ' + serverid))); - if ((serverid == null) && (obj.multiServer != null)) { obj.multiServer.DispatchMessage({ action: 'SetConnectivityState', meshid: meshid, nodeid: nodeid, connectTime: connectTime, connectType: connectType, powerState: powerState }); } + if ((serverid == null) && (obj.multiServer != null)) { obj.multiServer.DispatchMessage({ action: 'SetConnectivityState', meshid: meshid, nodeid: nodeid, connectTime: connectTime, connectType: connectType, powerState: powerState, extraInfo: extraInfo }); } if (obj.multiServer == null) { // Single server mode @@ -2131,6 +2171,9 @@ function CreateMeshCentralServer(config, args) { const lc = { _id: 'lc' + nodeid, type: 'lastconnect', domain: nodeid.split('/')[1], meshid: meshid, time: Date.now(), cause: 1, connectType: connectType }; if (extraInfo && extraInfo.remoteaddrport) { lc.addr = extraInfo.remoteaddrport; } obj.db.Set(lc); + + // Notify any users of device connection + obj.NotifyUserOfDeviceStateChange(meshid, nodeid, connectTime, connectType, powerState, serverid, true, extraInfo); } } else { // Multi server mode @@ -2175,6 +2218,9 @@ function CreateMeshCentralServer(config, args) { if (extraInfo && extraInfo.remoteaddrport) { lc.addr = extraInfo.remoteaddrport; } obj.db.Set(lc); } + + // Notify any users of device connection + obj.NotifyUserOfDeviceStateChange(meshid, nodeid, connectTime, connectType, powerState, serverid, true, extraInfo); } } }; @@ -2185,7 +2231,7 @@ function CreateMeshCentralServer(config, args) { // connectType: Bitmask, 1 = MeshAgent, 2 = Intel AMT CIRA, 3 = Intel AMT local. obj.ClearConnectivityState = function (meshid, nodeid, connectType, serverid, extraInfo) { //console.log('ClearConnectivity for ' + nodeid.substring(0, 16) + ', Type: ' + connectTypeStrings[connectType] + (serverid == null?(''):(', ServerId: ' + serverid))); - if ((serverid == null) && (obj.multiServer != null)) { obj.multiServer.DispatchMessage({ action: 'ClearConnectivityState', meshid: meshid, nodeid: nodeid, connectType: connectType }); } + if ((serverid == null) && (obj.multiServer != null)) { obj.multiServer.DispatchMessage({ action: 'ClearConnectivityState', meshid: meshid, nodeid: nodeid, connectType: connectType, extraInfo: extraInfo }); } if (obj.multiServer == null) { // Single server mode @@ -2223,6 +2269,9 @@ function CreateMeshCentralServer(config, args) { // Event the node connection change if (eventConnectChange == 1) { obj.DispatchEvent(obj.webserver.CreateNodeDispatchTargets(meshid, nodeid), obj, { action: 'nodeconnect', meshid: meshid, nodeid: nodeid, domain: nodeid.split('/')[1], conn: state.connectivity, pwr: state.powerState, nolog: 1, nopeers: 1 }); + + // Notify any users of device disconnection + obj.NotifyUserOfDeviceStateChange(meshid, nodeid, Date.now(), connectType, -1, serverid, false, extraInfo); } } else { // Multi server mode @@ -2246,6 +2295,9 @@ function CreateMeshCentralServer(config, args) { // If the node is completely disconnected, clean it up completely if (state.connectivity == 0) { delete obj.peerConnectivityByNode[serverid][nodeid]; state.powerState = 0; } + + // Notify any users of device disconnection + obj.NotifyUserOfDeviceStateChange(meshid, nodeid, Date.now(), connectType, -1, serverid, false, extraInfo); } // Clear node power state diff --git a/meshmail.js b/meshmail.js index 2b1fba08..20220dd0 100644 --- a/meshmail.js +++ b/meshmail.js @@ -448,5 +448,175 @@ module.exports.CreateMeshMail = function (parent, domain) { }); } + // + // Device connetion and disconnection notifications + // + + obj.deviceNotifications = {}; // UserId --> { timer, nodes: nodeid --> connectType } + + // A device connected and a user needs to be notified about it. + obj.notifyDeviceConnect = function (user, meshid, nodeid, connectTime, connectType, powerState, serverid, extraInfo) { + const mesh = parent.webserver.meshes[meshid]; + if (mesh == null) return; + + // Add the user and start a timer + if (obj.deviceNotifications[user._id] == null) { + obj.deviceNotifications[user._id] = { nodes: {} }; + obj.deviceNotifications[user._id].timer = setTimeout(function () { sendDeviceNotifications(user._id); }, 20000); + } + + // Add the device + if (obj.deviceNotifications[user._id].nodes[nodeid] == null) { + obj.deviceNotifications[user._id].nodes[nodeid] = { c: connectType }; // This device connection need to be added + } else { + const info = obj.deviceNotifications[user._id].nodes[nodeid]; + if ((info.d != null) && ((info.d & connectType) != 0)) { + info.d -= connectType; // This device disconnect cancels out a device connection + if (((info.c == null) || (info.c == 0)) && ((info.d == null) || (info.d == 0))) { + // This device no longer needs a notification + delete obj.deviceNotifications[user._id].nodes[nodeid]; + if (Object.keys(obj.deviceNotifications[user._id].nodes).length == 0) { + // This user no longer needs a notification + clearTimeout(obj.deviceNotifications[user._id].timer); + delete obj.deviceNotifications[user._id]; + } + return; + } + } else { + if (info.c != null) { + info.c |= connectType; // This device disconnect needs to be added + } else { + info.c = connectType; // This device disconnect needs to be added + } + } + } + + // Set the device group name + if ((extraInfo != null) && (extraInfo.name != null)) { obj.deviceNotifications[user._id].nodes[nodeid].nn = extraInfo.name; } + obj.deviceNotifications[user._id].nodes[nodeid].mn = mesh.name; + } + + // Cancel a device disconnect notification + obj.cancelNotifyDeviceDisconnect = function (user, meshid, nodeid, connectTime, connectType, powerState, serverid, extraInfo) { + const mesh = parent.webserver.meshes[meshid]; + if (mesh == null) return; + + if ((obj.deviceNotifications[user._id] != null) && (obj.deviceNotifications[user._id].nodes[nodeid] != null)) { + const info = obj.deviceNotifications[user._id].nodes[nodeid]; + if ((info.d != null) && ((info.d & connectType) != 0)) { + info.d -= connectType; // This device disconnect cancels out a device connection + if (((info.c == null) || (info.c == 0)) && ((info.d == null) || (info.d == 0))) { + // This device no longer needs a notification + delete obj.deviceNotifications[user._id].nodes[nodeid]; + if (Object.keys(obj.deviceNotifications[user._id].nodes).length == 0) { + // This user no longer needs a notification + clearTimeout(obj.deviceNotifications[user._id].timer); + delete obj.deviceNotifications[user._id]; + } + } + } + } + } + + // A device disconnected and a user needs to be notified about it. + obj.notifyDeviceDisconnect = function (user, meshid, nodeid, connectTime, connectType, powerState, serverid, extraInfo) { + const mesh = parent.webserver.meshes[meshid]; + if (mesh == null) return; + + // Add the user and start a timer + if (obj.deviceNotifications[user._id] == null) { + obj.deviceNotifications[user._id] = { nodes: {} }; + obj.deviceNotifications[user._id].timer = setTimeout(function () { sendDeviceNotifications(user._id); }, 20000); + } + + // Add the device + if (obj.deviceNotifications[user._id].nodes[nodeid] == null) { + obj.deviceNotifications[user._id].nodes[nodeid] = { d: connectType }; // This device disconnect need to be added + } else { + const info = obj.deviceNotifications[user._id].nodes[nodeid]; + if ((info.c != null) && ((info.c & connectType) != 0)) { + info.c -= connectType; // This device disconnect cancels out a device connection + if (((info.d == null) || (info.d == 0)) && ((info.c == null) || (info.c == 0))) { + // This device no longer needs a notification + delete obj.deviceNotifications[user._id].nodes[nodeid]; + if (Object.keys(obj.deviceNotifications[user._id].nodes).length == 0) { + // This user no longer needs a notification + clearTimeout(obj.deviceNotifications[user._id].timer); + delete obj.deviceNotifications[user._id]; + } + return; + } + } else { + if (info.d != null) { + info.d |= connectType; // This device disconnect needs to be added + } else { + info.d = connectType; // This device disconnect needs to be added + } + } + } + + // Set the device group name + if ((extraInfo != null) && (extraInfo.name != null)) { obj.deviceNotifications[user._id].nodes[nodeid].nn = extraInfo.name; } + obj.deviceNotifications[user._id].nodes[nodeid].mn = mesh.name; + } + + // Cancel a device connect notification + obj.cancelNotifyDeviceConnect = function (user, meshid, nodeid, connectTime, connectType, powerState, serverid, extraInfo) { + const mesh = parent.webserver.meshes[meshid]; + if (mesh == null) return; + + if ((obj.deviceNotifications[user._id] != null) && (obj.deviceNotifications[user._id].nodes[nodeid] != null)) { + const info = obj.deviceNotifications[user._id].nodes[nodeid]; + if ((info.c != null) && ((info.c & connectType) != 0)) { + info.c -= connectType; // This device disconnect cancels out a device connection + if (((info.d == null) || (info.d == 0)) && ((info.c == null) || (info.c == 0))) { + // This device no longer needs a notification + delete obj.deviceNotifications[user._id].nodes[nodeid]; + if (Object.keys(obj.deviceNotifications[user._id].nodes).length == 0) { + // This user no longer needs a notification + clearTimeout(obj.deviceNotifications[user._id].timer); + delete obj.deviceNotifications[user._id]; + } + } + } + } + } + + // Send a notification about device connections and disconnections to a user + function sendDeviceNotifications(userid) { + if (obj.deviceNotifications[userid] == null) return; + clearTimeout(obj.deviceNotifications[userid].timer); + + var connections = []; + var disconnections = []; + + for (var nodeid in obj.deviceNotifications[userid].nodes) { + var info = obj.deviceNotifications[userid].nodes[nodeid]; + if ((info.c != null) && (info.c > 0) && (info.nn != null) && (info.mn != null)) { + var c = []; + if (info.c & 1) { c.push("Agent"); } + if (info.c & 2) { c.push("CIRA"); } + if (info.c & 4) { c.push("AMT"); } + if (info.c & 8) { c.push("AMT-Relay"); } + if (info.c & 16) { c.push("MQTT"); } + connections.push(info.mn + ', ' + info.nn + ': ' + c.join(', ')); + } + if ((info.d != null) && (info.d > 0) && (info.nn != null) && (info.mn != null)) { + var d = []; + if (info.d & 1) { d.push("Agent"); } + if (info.d & 2) { d.push("CIRA"); } + if (info.d & 4) { d.push("AMT"); } + if (info.d & 8) { d.push("AMT-Relay"); } + if (info.d & 16) { d.push("MQTT"); } + disconnections.push(info.mn + ', ' + info.nn + ': ' + d.join(', ')); + } + } + + // TODO: Send the email + //console.log('sendDeviceNotifications', connections, disconnections); + + delete obj.deviceNotifications[userid]; + } + return obj; }; \ No newline at end of file diff --git a/meshuser.js b/meshuser.js index cbdf912a..779b591c 100644 --- a/meshuser.js +++ b/meshuser.js @@ -2112,6 +2112,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use { if ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 1024) != 0)) return; // If this account is settings locked, return here. + // 2 = WebPage device connections + // 4 = WebPage device disconnections + // 8 = WebPage device desktop and serial events + // 16 = Email device connections + // 32 = Email device disconnections + var err = null; try { // Change the current user's notification flags for a meshid diff --git a/mpsserver.js b/mpsserver.js index 26effacc..3b2de02d 100644 --- a/mpsserver.js +++ b/mpsserver.js @@ -164,10 +164,10 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { if ((ciraArray != null) && ((ciraArray.indexOf(setConnFunc.socket) >= 0))) { // Check if this connection is still present if (setConnFunc.socket.tag.connType == 0) { // Intel AMT CIRA connection. This connection indicates the remote device is present. - obj.parent.SetConnectivityState(setConnFunc.socket.tag.meshid, setConnFunc.socket.tag.nodeid, setConnFunc.socket.tag.connectTime, 2, 7); // 7 = Present + obj.parent.SetConnectivityState(setConnFunc.socket.tag.meshid, setConnFunc.socket.tag.nodeid, setConnFunc.socket.tag.connectTime, 2, 7, null, { name: socket.tag.name }); // 7 = Present } else if (setConnFunc.socket.tag.connType == 1) { // Intel AMT Relay connection. This connection does not give any information about the remote device's power state. - obj.parent.SetConnectivityState(setConnFunc.socket.tag.meshid, setConnFunc.socket.tag.nodeid, setConnFunc.socket.tag.connectTime, 8, 0); // 0 = Unknown + obj.parent.SetConnectivityState(setConnFunc.socket.tag.meshid, setConnFunc.socket.tag.nodeid, setConnFunc.socket.tag.connectTime, 8, 0, null, { name: socket.tag.name }); // 0 = Unknown } // Intel AMT LMS connection (connType == 2), we don't notify of these connections except telling the Intel AMT manager about them. // If the AMT manager is present, start management of this device @@ -201,9 +201,9 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { // Update connectivity state if (socket.tag.connType == 0) { - obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2); // CIRA + obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2, null, { name: socket.tag.name }); // CIRA } else if (socket.tag.connType == 1) { - obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 8); // Relay + obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 8, null, { name: socket.tag.name }); // Relay } } @@ -520,6 +520,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { // Node is already present var node = nodes[0]; socket.tag.meshid = node.meshid; // Correct the MeshID if the node has moved. + socket.tag.name = node.name; if ((node.intelamt != null) && (node.intelamt.state == 2)) { socket.tag.host = node.intelamt.host; } } @@ -727,6 +728,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) { // Node is already present var node = nodes[0]; socket.tag.meshid = node.meshid; + socket.tag.name = node.name; if ((node.intelamt != null) && (node.intelamt.state == 2)) { socket.tag.host = node.intelamt.host; } } diff --git a/mqttbroker.js b/mqttbroker.js index 26a9ca4f..c17dbdc4 100644 --- a/mqttbroker.js +++ b/mqttbroker.js @@ -69,7 +69,7 @@ module.exports.CreateMQTTBroker = function (parent, db, args) { if (obj.connections[client.xdbNodeKey] == null) { obj.connections[client.xdbNodeKey] = [client]; - parent.SetConnectivityState(client.xdbMeshKey, client.xdbNodeKey, Date.now(), 16, 7); // Indicate this node has a MQTT connection, 7 = Present state + parent.SetConnectivityState(client.xdbMeshKey, client.xdbNodeKey, Date.now(), 16, 7, null, { name: nodes[0].name }); // Indicate this node has a MQTT connection, 7 = Present state } else { obj.connections[client.xdbNodeKey].push(client); } @@ -85,7 +85,7 @@ module.exports.CreateMQTTBroker = function (parent, db, args) { if (i >= 0) { if (clients.length == 1) { delete obj.connections[this.parent.xdbNodeKey]; - parent.ClearConnectivityState(this.parent.xdbMeshKey, this.parent.xdbNodeKey, 16); // Remove the MQTT connection for this node + parent.ClearConnectivityState(this.parent.xdbMeshKey, this.parent.xdbNodeKey, 16, null, { name: nodes[0].name }); // Remove the MQTT connection for this node } else { clients.splice(i, 1); } } } diff --git a/multiserver.js b/multiserver.js index ed210374..bdcaa5e7 100644 --- a/multiserver.js +++ b/multiserver.js @@ -517,11 +517,11 @@ module.exports.CreateMultiServer = function (parent, args) { break; } case 'SetConnectivityState': { - obj.parent.SetConnectivityState(msg.meshid, msg.nodeid, msg.connectTime, msg.connectType, msg.powerState, peerServerId); + obj.parent.SetConnectivityState(msg.meshid, msg.nodeid, msg.connectTime, msg.connectType, msg.powerState, peerServerId, msg.extraInfo); break; } case 'ClearConnectivityState': { - obj.parent.ClearConnectivityState(msg.meshid, msg.nodeid, msg.connectType, peerServerId); + obj.parent.ClearConnectivityState(msg.meshid, msg.nodeid, msg.connectType, peerServerId, msg.extraInfo); break; } case 'relay': { diff --git a/translate/translate.json b/translate/translate.json index b2d1bc68..53f34282 100644 --- a/translate/translate.json +++ b/translate/translate.json @@ -18334,21 +18334,21 @@ { "cs": "Připojení zařízení", "de": "Geräteverbindungen", - "en": "Device connections.", - "es": "Conexiones de dispositivo.", - "fi": "Laiteyhteydet.", - "fr": "Connexions des appareils.", + "en": "Device connections", + "es": "Conexiones de dispositivo", + "fi": "Laiteyhteydet", + "fr": "Connexions des appareils", "hi": "डिवाइस कनेक्शन।", - "it": "Connessioni del dispositivo.", + "it": "Connessioni del dispositivo", "ja": "デバイス接続。", - "ko": "장치가 연결되었습니다.", - "nl": "Apparaat verbindingen.", - "pl": "Połączenia urządzenia.", - "pt": "Conexões de dispositivos.", - "pt-br": "Conexões de dispositivos.", - "ru": "Подключения устройств.", - "sv": "Enhetsanslutningar.", - "tr": "Cihaz bağlantıları.", + "ko": "장치가 연결되었습니다", + "nl": "Apparaat verbindingen", + "pl": "Połączenia urządzenia", + "pt": "Conexões de dispositivos", + "pt-br": "Conexões de dispositivos", + "ru": "Подключения устройств", + "sv": "Enhetsanslutningar", + "tr": "Cihaz bağlantıları", "zh-chs": "设备连接。", "zh-cht": "裝置連接。", "xloc": [ @@ -18359,21 +18359,21 @@ { "cs": "Odpojení zařízení", "de": "Gerätetrennungen", - "en": "Device disconnections.", - "es": "Desconexiones de dispositivo.", - "fi": "Laitteen irtikytkennät.", - "fr": "Déconnexions des appareils.", + "en": "Device disconnections", + "es": "Desconexiones de dispositivo", + "fi": "Laitteen irtikytkennät", + "fr": "Déconnexions des appareils", "hi": "डिवाइस डिस्कनेक्ट।", - "it": "Disconnessioni del dispositivo.", + "it": "Disconnessioni del dispositivo", "ja": "デバイスの切断。", - "ko": "장치 연결이 끊어졌습니다.", - "nl": "Apparaat verbroken.", - "pl": "Odłączenia urządzenia.", - "pt": "Desconexões de dispositivos.", - "pt-br": "Desconexões de dispositivos.", - "ru": "Отключения устройств.", - "sv": "Avbrott av enhet.", - "tr": "Cihaz bağlantılarının kesilmesi.", + "ko": "장치 연결이 끊어졌습니다", + "nl": "Apparaat verbroken", + "pl": "Odłączenia urządzenia", + "pt": "Desconexões de dispositivos", + "pt-br": "Desconexões de dispositivos", + "ru": "Отключения устройств", + "sv": "Avbrott av enhet", + "tr": "Cihaz bağlantılarının kesilmesi", "zh-chs": "设备断开连接。", "zh-cht": "裝置斷開連接。", "xloc": [ @@ -29049,25 +29049,25 @@ ] }, { - "cs": "Události Intel® AMT desktop a serial.", + "cs": "Události Intel® AMT desktop a serial", "de": "Intel® AMT Desktop- und Seriell-Ereignisse", - "en": "Intel® AMT desktop and serial events.", - "es": "Eventos de escritorio y serial de Intel® AMT.", - "fi": "Intel® AMT työpöytä ja sarjaliikenne ilmoitukset.", - "fr": "Événements de bureau et série Intel® AMT.", + "en": "Intel® AMT desktop and serial events", + "es": "Eventos de escritorio y serial de Intel® AMT", + "fi": "Intel® AMT työpöytä ja sarjaliikenne ilmoitukset", + "fr": "Événements de bureau et série Intel® AMT", "hi": "Intel® AMT डेस्कटॉप और सीरियल इवेंट।", - "it": "ntel & reg; Desktop AMT e eventi seriali.", + "it": "ntel & reg; Desktop AMT e eventi seriali", "ja": "Intel® AMTデスクトップおよびシリアルイベント。", - "ko": "Intel® AMT 데스크탑 및 직렬 이벤트.", - "nl": "Intel® AMT desktop- en seriële gebeurtenissen.", - "pl": "Zdarzenia pulpitu i połączeń przewodowych Intel® AMT.", - "pt": "Intel® Área de trabalho AMT e eventos seriais.", - "pt-br": "Desktop Intel® AMT e eventos seriais.", - "ru": "События Intel® AMT desktop или serial.", - "sv": "Intel® AMT stationära och seriella händelser.", - "tr": "Intel® AMT masaüstü ve seri etkinlikler.", - "zh-chs": "英特尔®AMT桌面和串行事件。", - "zh-cht": "Intel® AMT桌面和串行事件。", + "ko": "Intel® AMT 데스크탑 및 직렬 이벤트", + "nl": "Intel® AMT desktop- en seriële gebeurtenissen", + "pl": "Zdarzenia pulpitu i połączeń przewodowych Intel® AMT", + "pt": "Intel® Área de trabalho AMT e eventos seriais", + "pt-br": "Desktop Intel® AMT e eventos seriais", + "ru": "События Intel® AMT desktop или serial", + "sv": "Intel® AMT stationära och seriella händelser", + "tr": "Intel® AMT masaüstü ve seri etkinlikler", + "zh-chs": "英特尔®AMT桌面和串行事件", + "zh-cht": "Intel® AMT桌面和串行事件", "xloc": [ "default.handlebars->37->1640", "default.handlebars->37->1944" @@ -40258,25 +40258,25 @@ ] }, { - "cs": "Zvuk upozornění.", + "cs": "Zvuk upozornění", "de": "Benachrichtigungston", - "en": "Notification sound.", - "es": "Sonido de notificación.", - "fi": "Ilmoitusääni.", - "fr": "Notification sonore.", + "en": "Notification sound", + "es": "Sonido de notificación", + "fi": "Ilmoitusääni", + "fr": "Notification sonore", "hi": "सूचना ध्वनि।", - "it": "Suono di notifica.", - "ja": "通知音。", - "ko": "알림 소리.", - "nl": "Meldingsgeluid.", - "pl": "Powiadomienie dźwiękowe.", - "pt": "Som de notificação.", - "pt-br": "Som de notificação.", + "it": "Suono di notifica", + "ja": "通知音", + "ko": "알림 소리", + "nl": "Meldingsgeluid", + "pl": "Powiadomienie dźwiękowe", + "pt": "Som de notificação", + "pt-br": "Som de notificação", "ru": "Звук уведомления", - "sv": "Notifikations ljud.", - "tr": "Bilgilendirme sesi.", - "zh-chs": "通知音效。", - "zh-cht": "通知音效。", + "sv": "Notifikations ljud", + "tr": "Bilgilendirme sesi", + "zh-chs": "通知音效", + "zh-cht": "通知音效", "xloc": [ "default.handlebars->37->1636" ] diff --git a/views/default.handlebars b/views/default.handlebars index 666618af..ba00aae9 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -10931,11 +10931,11 @@ function account_showAccountNotifySettings() { if (xxdialogMode) return false; var x = ''; - x += '
'; + x += '
'; x += '
'; - x += '
'; - x += '
'; - x += '
'; + x += '
'; + x += '
'; + x += '
'; setDialogMode(2, "Notification Settings", 3, account_showAccountNotifySettingsEx, x); var n = getstore('notifications', 0); Q('p2notifyPlayNotifySound').checked = (n & 1); @@ -11344,6 +11344,10 @@ if (meshNotify & 2) { meshNotifyStr.push("Connect"); } if (meshNotify & 4) { meshNotifyStr.push("Disconnect"); } if (meshNotify & 8) { meshNotifyStr.push("Intel® AMT"); } + if ((features2 & 0x00004000) && (userinfo.emailVerified)) { + if (meshNotify & 16) { meshNotifyStr.push("Email Connect"); } + if (meshNotify & 32) { meshNotifyStr.push("Email Disconnect"); } + } if (meshNotifyStr.length == 0) { meshNotifyStr.push('' + "None" + ''); } x += addHtmlValue("Notifications", addLink(meshNotifyStr.join(', '), 'p20editMeshNotify()')); } @@ -12262,23 +12266,38 @@ function p20editMeshNotify() { if (xxdialogMode) return false; var meshNotify = 0; + var emailNotify = ((features2 & 0x00004000) && (userinfo.emailVerified)); if (userinfo.links && userinfo.links[currentMesh._id] && userinfo.links[currentMesh._id].notify) { meshNotify = userinfo.links[currentMesh._id].notify; } - var x = "Notification settings must also be turned on in account settings." + '

'; - x += '
'; - x += '
'; - x += '
'; - setDialogMode(2, "Notification Settings", 3, p20editMeshNotifyEx, x); + //var x = "Notification settings must also be turned on in account settings." + '

'; + var x = '
Web Page Notifications
'; + x += '
'; + x += '
'; + x += '
'; + if (emailNotify) { + x += '
Email Notifications
'; + x += '
'; + x += '
'; + } + setDialogMode(2, "Notification Settings", 3, p20editMeshNotifyEx, x, emailNotify); Q('p20notifyIntelDeviceConnect').checked = (meshNotify & 2); Q('p20notifyIntelDeviceDisconnect').checked = (meshNotify & 4); Q('p20notifyIntelAmtKvmActions').checked = (meshNotify & 8); + if (emailNotify) { + Q('p20enotifyIntelDeviceConnect').checked = (meshNotify & 16); + Q('p20enotifyIntelDeviceDisconnect').checked = (meshNotify & 32); + } return false; } - function p20editMeshNotifyEx() { + function p20editMeshNotifyEx(b, emailNotify) { var meshNotify = 0; meshNotify += Q('p20notifyIntelDeviceConnect').checked ? 2 : 0; meshNotify += Q('p20notifyIntelDeviceDisconnect').checked ? 4 : 0; meshNotify += Q('p20notifyIntelAmtKvmActions').checked ? 8 : 0; + if (emailNotify) { + meshNotify += Q('p20enotifyIntelDeviceConnect').checked ? 16 : 0; + meshNotify += Q('p20enotifyIntelDeviceDisconnect').checked ? 32 : 0; + } meshserver.send({ action: 'changemeshnotify', meshid: currentMesh._id, notify: meshNotify }); } diff --git a/webserver.js b/webserver.js index 8061f4d1..dbd654bc 100644 --- a/webserver.js +++ b/webserver.js @@ -2829,6 +2829,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (domain.clipboardget == false) { features2 += 0x00000800; } // Disable clipboard get if (domain.clipboardset == false) { features2 += 0x00001000; } // Disable clipboard set if ((typeof domain.desktop == 'object') && (domain.desktop.viewonly == true)) { features2 += 0x00002000; } // Indicates remote desktop is viewonly + if (domain.mailserver != null) { features2 += 0x00004000; } // Indicates email server is active return { features: features, features2: features2 }; }