Added agent installation invite using email.
Ylian Saint-Hilaire committed
Jan 23, 2018 at 14:15 UTC
c3144c097a7de27576b22686cc0c7cc4e520d71d
19 files changed
+387
-151
MeshCentralServer.njsproj
+8
@@ -21,8 +21,16 @@
21
<PropertyGroup Condition="'$(Configuration)' == 'Release'" />
22
<ItemGroup>
23
<Compile Include="agents\meshcore.js" />
24
+ <Compile Include="agents\modules_meshcmd\amt-0.2.0.js" />
25
+ <Compile Include="agents\modules_meshcmd\amt-scanner.js" />
26
+ <Compile Include="agents\modules_meshcmd\amt-script-0.2.0.js" />
27
+ <Compile Include="agents\modules_meshcmd\amt-wsman-0.2.0.js" />
28
+ <Compile Include="agents\modules_meshcmd\amt-wsman-duk-0.2.0.js" />
29
<Compile Include="agents\modules_meshcmd\amt_heci.js" />
30
+ <Compile Include="agents\modules_meshcmd\lme_heci.js" />
31
+ <Compile Include="agents\modules_meshcore\amt-scanner.js" />
32
<Compile Include="agents\modules_meshcore\amt_heci.js" />
33
+ <Compile Include="agents\modules_meshcore\lme_heci.js" />
34
<Compile Include="agents\modules_meshcore\WifiScanner.js" />
35
<Compile Include="agents\tinycore.js" />
36
<Compile Include="amtevents.js" />
agents/meshcore.js
+6
-2
@@ -719,6 +719,7 @@ function createMeshCore(agent) {
719
// Other side received websocket end of data marker, start sending data on WebRTC channel
720
if (this.httprequest.protocol == 1) { // Terminal
721
this.httprequest.process.stdout.pipe(this.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
722
+ this.httprequest.process.stderr.pipe(this.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
723
} else if (this.httprequest.protocol == 2) { // Desktop
724
this.httprequest.desktop.kvm.pipe(this.webrtc.rtcchannel, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
725
}
@@ -731,12 +732,15 @@ function createMeshCore(agent) {
732
this.webrtc.on('dataChannel', function (rtcchannel) {
733
sendConsoleText('WebRTC Datachannel open, protocol: ' + this.websocket.httprequest.protocol);
734
rtcchannel.xrtc = this;
735
+ rtcchannel.websocket = this.websocket;
736
this.rtcchannel = rtcchannel;
735
- this.rtcchannel.on('data', onTunnelWebRTCControlData);
736
- this.rtcchannel.on('end', function () { sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed'); });
737
+ this.websocket.rtcchannel = rtcchannel;
738
+ this.websocket.rtcchannel.on('data', onTunnelWebRTCControlData);
739
+ this.websocket.rtcchannel.on('end', function () { sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed'); });
740
if (this.websocket.httprequest.protocol == 1) { // Terminal
741
// This is a terminal data stream, unpipe the terminal now and indicate to the other side that terminal data will no longer be received over WebSocket
742
this.websocket.httprequest.process.stdout.unpipe(this.websocket);
743
+ this.websocket.httprequest.process.stderr.unpipe(this.websocket);
744
this.websocket.write("{\"type\":\"webrtc1\"}"); // End of data marker
745
} else if (this.websocket.httprequest.protocol == 2) { // Desktop
746
// This is a KVM data stream, unpipe the KVM now and indicate to the other side that KVM data will no longer be received over WebSocket
agents/modules_meshcmd/amt-0.2.0.js
+16
@@ -1,3 +1,19 @@
1
+/*
2
+Copyright 2018 Intel Corporation
3
+
4
+Licensed under the Apache License, Version 2.0 (the "License");
5
+you may not use this file except in compliance with the License.
6
+You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+Unless required by applicable law or agreed to in writing, software
11
+distributed under the License is distributed on an "AS IS" BASIS,
12
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+See the License for the specific language governing permissions and
14
+limitations under the License.
15
+*/
16
+
17
/**
18
* @fileoverview Intel(r) AMT Communication StackXX
19
* @author Ylian Saint-Hilaire
agents/modules_meshcmd/amt-scanner.js
+16
@@ -1,3 +1,19 @@
1
+/*
2
+Copyright 2018 Intel Corporation
3
+
4
+Licensed under the Apache License, Version 2.0 (the "License");
5
+you may not use this file except in compliance with the License.
6
+You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+Unless required by applicable law or agreed to in writing, software
11
+distributed under the License is distributed on an "AS IS" BASIS,
12
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+See the License for the specific language governing permissions and
14
+limitations under the License.
15
+*/
16
+
17
/**
18
* @description Meshcentral Intel AMT Local Scanner
19
* @author Ylian Saint-Hilaire & Joko Sastriawan
agents/modules_meshcmd/amt-script-0.2.0.js
+16
@@ -1,3 +1,19 @@
1
+/*
2
+Copyright 2018 Intel Corporation
3
+
4
+Licensed under the Apache License, Version 2.0 (the "License");
5
+you may not use this file except in compliance with the License.
6
+You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+Unless required by applicable law or agreed to in writing, software
11
+distributed under the License is distributed on an "AS IS" BASIS,
12
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+See the License for the specific language governing permissions and
14
+limitations under the License.
15
+*/
16
+
17
/**
18
* @fileoverview Script Compiler / Decompiler / Runner
19
* @author Ylian Saint-Hilaire
agents/modules_meshcmd/amt-wsman-0.2.0.js
+16
@@ -1,3 +1,19 @@
1
+/*
2
+Copyright 2018 Intel Corporation
3
+
4
+Licensed under the Apache License, Version 2.0 (the "License");
5
+you may not use this file except in compliance with the License.
6
+You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+Unless required by applicable law or agreed to in writing, software
11
+distributed under the License is distributed on an "AS IS" BASIS,
12
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+See the License for the specific language governing permissions and
14
+limitations under the License.
15
+*/
16
+
17
/**
18
* @description Intel(r) AMT WSMAN Stack
19
* @author Ylian Saint-Hilaire
agents/modules_meshcmd/amt-wsman-duk-0.2.0.js
+17
-1
@@ -1,4 +1,20 @@
1
-/**
1
+/*
2
+Copyright 2018 Intel Corporation
3
+
4
+Licensed under the Apache License, Version 2.0 (the "License");
5
+you may not use this file except in compliance with the License.
6
+You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+Unless required by applicable law or agreed to in writing, software
11
+distributed under the License is distributed on an "AS IS" BASIS,
12
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+See the License for the specific language governing permissions and
14
+limitations under the License.
15
+*/
16
+
17
+/**
18
* @description WSMAN communication using duktape http
19
* @author Ylian Saint-Hilaire
20
* @version v0.2.0c
agents/modules_meshcmd/amt_heci.js
+16
@@ -1,3 +1,19 @@
1
+/*
2
+Copyright 2018 Intel Corporation
3
+
4
+Licensed under the Apache License, Version 2.0 (the "License");
5
+you may not use this file except in compliance with the License.
6
+You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+Unless required by applicable law or agreed to in writing, software
11
+distributed under the License is distributed on an "AS IS" BASIS,
12
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+See the License for the specific language governing permissions and
14
+limitations under the License.
15
+*/
16
+
17
var Q = require('queue');
18
19
function amt_heci() {
agents/modules_meshcmd/lme_heci.js
+16
-1
@@ -1,3 +1,18 @@
1
+/*
2
+Copyright 2018 Intel Corporation
3
+
4
+Licensed under the Apache License, Version 2.0 (the "License");
5
+you may not use this file except in compliance with the License.
6
+You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+Unless required by applicable law or agreed to in writing, software
11
+distributed under the License is distributed on an "AS IS" BASIS,
12
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+See the License for the specific language governing permissions and
14
+limitations under the License.
15
+*/
16
17
var MemoryStream = require('MemoryStream');
18
var lme_id = 0;
@@ -151,7 +166,7 @@ function lme_heci() {
166
}
167
this[name][port] = require('net').createServer();
168
this[name][port].HECI = this;
154
- this[name][port].listen({ port: port });
169
+ this[name][port].listen({ port: port, host: '127.0.0.1' });
170
this[name][port].on('connection', function (socket) {
171
//console.log('New [' + socket.remoteFamily + '] TCP Connection on: ' + socket.remoteAddress + ' :' + socket.localPort);
172
this.HECI.LMS.bindDuplexStream(socket, socket.remoteFamily, socket.localPort);
agents/modules_meshcore/WifiScanner.js
+16
@@ -1,3 +1,19 @@
1
+/*
2
+Copyright 2018 Intel Corporation
3
+
4
+Licensed under the Apache License, Version 2.0 (the "License");
5
+you may not use this file except in compliance with the License.
6
+You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+Unless required by applicable law or agreed to in writing, software
11
+distributed under the License is distributed on an "AS IS" BASIS,
12
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+See the License for the specific language governing permissions and
14
+limitations under the License.
15
+*/
16
+
17
var MemoryStream = require('MemoryStream');
18
var WindowsWireless = new Buffer([
19
0x0A, 0x66, 0x75, 0x6E, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x5F, 0x53, 0x63, 0x61, 0x6E, 0x28, 0x29, 0x0A, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x76, 0x61, 0x72, 0x20, 0x77, 0x6C, 0x61, 0x6E,
agents/modules_meshcore/amt-scanner.js
+16
@@ -1,3 +1,19 @@
1
+/*
2
+Copyright 2018 Intel Corporation
3
+
4
+Licensed under the Apache License, Version 2.0 (the "License");
5
+you may not use this file except in compliance with the License.
6
+You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+Unless required by applicable law or agreed to in writing, software
11
+distributed under the License is distributed on an "AS IS" BASIS,
12
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+See the License for the specific language governing permissions and
14
+limitations under the License.
15
+*/
16
+
17
/**
18
* @description Meshcentral Intel AMT Local Scanner
19
* @author Ylian Saint-Hilaire & Joko Sastriawan
agents/modules_meshcore/amt_heci.js
+16
@@ -1,3 +1,19 @@
1
+/*
2
+Copyright 2018 Intel Corporation
3
+
4
+Licensed under the Apache License, Version 2.0 (the "License");
5
+you may not use this file except in compliance with the License.
6
+You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+Unless required by applicable law or agreed to in writing, software
11
+distributed under the License is distributed on an "AS IS" BASIS,
12
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+See the License for the specific language governing permissions and
14
+limitations under the License.
15
+*/
16
+
17
var Q = require('queue');
18
19
function amt_heci() {
agents/modules_meshcore/lme_heci.js
+105
-130
@@ -1,3 +1,18 @@
1
+/*
2
+Copyright 2018 Intel Corporation
3
+
4
+Licensed under the Apache License, Version 2.0 (the "License");
5
+you may not use this file except in compliance with the License.
6
+You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+Unless required by applicable law or agreed to in writing, software
11
+distributed under the License is distributed on an "AS IS" BASIS,
12
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+See the License for the specific language governing permissions and
14
+limitations under the License.
15
+*/
16
17
var MemoryStream = require('MemoryStream');
18
var lme_id = 0;
@@ -21,8 +36,7 @@ var APF_CHANNEL_CLOSE = 97;
36
var APF_PROTOCOLVERSION = 192;
37
38
24
-function lme_object()
25
-{
39
+function lme_object() {
40
this.ourId = ++lme_id;
41
this.amtId = -1;
42
this.LME_CHANNEL_STATUS = 'LME_CS_FREE';
@@ -32,12 +46,11 @@ function lme_object()
46
this.errorCount = 0;
47
}
48
35
-function stream_bufferedWrite()
36
-{
49
+function stream_bufferedWrite() {
50
var emitterUtils = require('events').inherits(this);
51
this.buffer = [];
52
this._readCheckImmediate = undefined;
40
-
53
+
54
// Writable Events
55
emitterUtils.createEvent('close');
56
emitterUtils.createEvent('drain');
@@ -45,21 +58,17 @@ function stream_bufferedWrite()
58
emitterUtils.createEvent('finish');
59
emitterUtils.createEvent('pipe');
60
emitterUtils.createEvent('unpipe');
48
-
61
+
62
// Readable Events
63
emitterUtils.createEvent('readable');
51
- this.isEmpty = function ()
52
- {
64
+ this.isEmpty = function () {
65
return (this.buffer.length == 0);
66
};
55
- this.isWaiting = function ()
56
- {
67
+ this.isWaiting = function () {
68
return (this._readCheckImmediate == undefined);
69
};
59
- this.write = function (chunk)
60
- {
61
- for (var args in arguments)
62
- {
70
+ this.write = function (chunk) {
71
+ for (var args in arguments) {
72
if (typeof (arguments[args]) == 'function') { this.once('drain', arguments[args]); break; }
73
}
74
var tmp = Buffer.alloc(chunk.length);
@@ -68,41 +77,34 @@ function stream_bufferedWrite()
77
this.emit('readable');
78
return (this.buffer.length == 0 ? true : false);
79
};
71
- this.read = function ()
72
- {
80
+ this.read = function () {
81
var size = arguments.length == 0 ? undefined : arguments[0];
82
var bytesRead = 0;
83
var list = [];
76
- while((size == undefined || bytesRead < size) && this.buffer.length > 0)
77
- {
84
+ while ((size == undefined || bytesRead < size) && this.buffer.length > 0) {
85
var len = this.buffer[0].data.length - this.buffer[0].offset;
86
var offset = this.buffer[0].offset;
80
-
81
- if(len > (size - bytesRead))
82
- {
87
+
88
+ if (len > (size - bytesRead)) {
89
// Only reading a subset
90
list.push(this.buffer[0].data.slice(offset, offset + size - bytesRead));
91
this.buffer[0].offset += (size - bytesRead);
92
bytesRead += (size - bytesRead);
93
}
88
- else
89
- {
94
+ else {
95
// Reading the entire thing
96
list.push(this.buffer[0].data.slice(offset));
97
bytesRead += len;
98
this.buffer.shift();
99
}
100
}
96
- this._readCheckImmediate = setImmediate(function (buffered)
97
- {
101
+ this._readCheckImmediate = setImmediate(function (buffered) {
102
buffered._readCheckImmediate = undefined;
99
- if(buffered.buffer.length == 0)
100
- {
103
+ if (buffered.buffer.length == 0) {
104
// drained
105
buffered.emit('drain');
106
}
104
- else
105
- {
107
+ else {
108
// not drained
109
buffered.emit('readable');
110
}
@@ -112,38 +114,33 @@ function stream_bufferedWrite()
114
}
115
116
115
-function lme_heci()
116
-{
117
+function lme_heci() {
118
var emitterUtils = require('events').inherits(this);
119
emitterUtils.createEvent('error');
120
emitterUtils.createEvent('connect');
120
-
121
+
122
var heci = require('heci');
123
this.INITIAL_RXWINDOW_SIZE = 4096;
123
-
124
+
125
this._LME = heci.create();
126
this._LME.LMS = this;
127
this._LME.on('error', function (e) { this.LMS.emit('error', e); });
127
- this._LME.on('connect', function ()
128
- {
128
+ this._LME.on('connect', function () {
129
this.LMS.emit('connect');
130
- this.on('data', function (chunk)
131
- {
130
+ this.on('data', function (chunk) {
131
// this = HECI
132
var cmd = chunk.readUInt8(0);
134
-
135
- switch(cmd)
136
- {
133
+
134
+ switch (cmd) {
135
default:
136
//console.log('Received ' + chunk.length + ' bytes of data for LMS');
137
//console.log('Command = ' + cmd);
138
break;
141
- case APF_SERVICE_REQUEST:
139
+ case APF_SERVICE_REQUEST:
140
var nameLen = chunk.readUInt32BE(1);
141
var name = chunk.slice(5, nameLen + 5);
142
//console.log("Service Request for: " + name);
145
- if (name == 'pfwd@amt.intel.com' || name == 'auth@amt.intel.com')
146
- {
143
+ if (name == 'pfwd@amt.intel.com' || name == 'auth@amt.intel.com') {
144
var outBuffer = Buffer.alloc(5 + nameLen);
145
outBuffer.writeUInt8(6, 0);
146
outBuffer.writeUInt32BE(nameLen, 1);
@@ -151,30 +148,26 @@ function lme_heci()
148
this.write(outBuffer);
149
//console.log('Answering APF_SERVICE_REQUEST');
150
}
154
- else
155
- {
151
+ else {
152
//console.log('UNKNOWN APF_SERVICE_REQUEST');
153
}
154
break;
159
- case APF_GLOBAL_REQUEST:
155
+ case APF_GLOBAL_REQUEST:
156
var nameLen = chunk.readUInt32BE(1);
157
var name = chunk.slice(5, nameLen + 5).toString();
158
163
- switch(name)
164
- {
159
+ switch (name) {
160
case 'tcpip-forward':
161
var len = chunk.readUInt32BE(nameLen + 6);
162
var port = chunk.readUInt32BE(nameLen + 10 + len);
163
//console.log("[" + chunk.length + "/" + len + "] APF_GLOBAL_REQUEST for: " + name + " on port " + port);
169
- if (this[name] == undefined)
170
- {
164
+ if (this[name] == undefined) {
165
this[name] = {};
166
}
167
this[name][port] = require('net').createServer();
168
this[name][port].HECI = this;
175
- this[name][port].listen({ port: port });
176
- this[name][port].on('connection', function (socket)
177
- {
169
+ this[name][port].listen({ port: port, host: '127.0.0.1' });
170
+ this[name][port].on('connection', function (socket) {
171
//console.log('New [' + socket.remoteFamily + '] TCP Connection on: ' + socket.remoteAddress + ' :' + socket.localPort);
172
this.HECI.LMS.bindDuplexStream(socket, socket.remoteFamily, socket.localPort);
173
});
@@ -192,54 +185,48 @@ function lme_heci()
185
break;
186
}
187
break;
195
- case APF_CHANNEL_OPEN_CONFIRMATION:
196
- var rChannel = chunk.readUInt32BE(1);
197
- var sChannel = chunk.readUInt32BE(5);
198
- var wSize = chunk.readUInt32BE(9);
199
- //console.log('rChannel/' + rChannel + ', sChannel/' + sChannel + ', wSize/' + wSize);
200
- if (this.sockets[rChannel] != undefined)
201
- {
202
- this.sockets[rChannel].lme.amtId = sChannel;
203
- this.sockets[rChannel].lme.rxWindow = wSize;
204
- this.sockets[rChannel].lme.txWindow = wSize;
205
- this.sockets[rChannel].lme.LME_CHANNEL_STATUS = 'LME_CS_CONNECTED';
206
- //console.log('LME_CS_CONNECTED');
207
- this.sockets[rChannel].bufferedStream = new stream_bufferedWrite();
208
- this.sockets[rChannel].bufferedStream.socket = this.sockets[rChannel];
209
- this.sockets[rChannel].bufferedStream.on('readable', function ()
210
- {
211
- if(this.socket.lme.txWindow > 0)
212
- {
213
- var buffer = this.read(this.socket.lme.txWindow);
214
- var packet = Buffer.alloc(9 + buffer.length);
215
- packet.writeUInt8(APF_CHANNEL_DATA, 0);
216
- packet.writeUInt32BE(this.socket.lme.amtId, 1);
217
- packet.writeUInt32BE(buffer.length, 5);
218
- buffer.copy(packet, 9);
219
- this.socket.lme.txWindow -= buffer.length;
220
- this.socket.HECI.write(packet);
221
- }
222
- });
223
- this.sockets[rChannel].bufferedStream.on('drain', function ()
224
- {
225
- this.socket.resume();
226
- });
227
- this.sockets[rChannel].on('data', function (chunk)
228
- {
229
- if (!this.bufferedStream.write(chunk)) { this.pause(); }
230
- });
231
- this.sockets[rChannel].on('end', function ()
232
- {
233
- var outBuffer = Buffer.alloc(5);
234
- outBuffer.writeUInt8(APF_CHANNEL_CLOSE, 0);
235
- outBuffer.writeUInt32BE(this.lme.amtId, 1);
236
- this.HECI.write(outBuffer);
237
- });
238
- this.sockets[rChannel].resume();
239
- }
240
-
241
- break;
242
- case APF_PROTOCOLVERSION:
188
+ case APF_CHANNEL_OPEN_CONFIRMATION:
189
+ var rChannel = chunk.readUInt32BE(1);
190
+ var sChannel = chunk.readUInt32BE(5);
191
+ var wSize = chunk.readUInt32BE(9);
192
+ //console.log('rChannel/' + rChannel + ', sChannel/' + sChannel + ', wSize/' + wSize);
193
+ if (this.sockets[rChannel] != undefined) {
194
+ this.sockets[rChannel].lme.amtId = sChannel;
195
+ this.sockets[rChannel].lme.rxWindow = wSize;
196
+ this.sockets[rChannel].lme.txWindow = wSize;
197
+ this.sockets[rChannel].lme.LME_CHANNEL_STATUS = 'LME_CS_CONNECTED';
198
+ //console.log('LME_CS_CONNECTED');
199
+ this.sockets[rChannel].bufferedStream = new stream_bufferedWrite();
200
+ this.sockets[rChannel].bufferedStream.socket = this.sockets[rChannel];
201
+ this.sockets[rChannel].bufferedStream.on('readable', function () {
202
+ if (this.socket.lme.txWindow > 0) {
203
+ var buffer = this.read(this.socket.lme.txWindow);
204
+ var packet = Buffer.alloc(9 + buffer.length);
205
+ packet.writeUInt8(APF_CHANNEL_DATA, 0);
206
+ packet.writeUInt32BE(this.socket.lme.amtId, 1);
207
+ packet.writeUInt32BE(buffer.length, 5);
208
+ buffer.copy(packet, 9);
209
+ this.socket.lme.txWindow -= buffer.length;
210
+ this.socket.HECI.write(packet);
211
+ }
212
+ });
213
+ this.sockets[rChannel].bufferedStream.on('drain', function () {
214
+ this.socket.resume();
215
+ });
216
+ this.sockets[rChannel].on('data', function (chunk) {
217
+ if (!this.bufferedStream.write(chunk)) { this.pause(); }
218
+ });
219
+ this.sockets[rChannel].on('end', function () {
220
+ var outBuffer = Buffer.alloc(5);
221
+ outBuffer.writeUInt8(APF_CHANNEL_CLOSE, 0);
222
+ outBuffer.writeUInt32BE(this.lme.amtId, 1);
223
+ this.HECI.write(outBuffer);
224
+ });
225
+ this.sockets[rChannel].resume();
226
+ }
227
+
228
+ break;
229
+ case APF_PROTOCOLVERSION:
230
var major = chunk.readUInt32BE(1);
231
var minor = chunk.readUInt32BE(5);
232
var reason = chunk.readUInt32BE(9);
@@ -254,16 +241,13 @@ function lme_heci()
241
case APF_CHANNEL_WINDOW_ADJUST:
242
var rChannelId = chunk.readUInt32BE(1);
243
var bytesToAdd = chunk.readUInt32BE(5);
257
- if (this.sockets[rChannelId] != undefined)
258
- {
244
+ if (this.sockets[rChannelId] != undefined) {
245
this.sockets[rChannelId].lme.txWindow += bytesToAdd;
260
- if (!this.sockets[rChannelId].bufferedStream.isEmpty() && this.sockets[rChannelId].bufferedStream.isWaiting())
261
- {
246
+ if (!this.sockets[rChannelId].bufferedStream.isEmpty() && this.sockets[rChannelId].bufferedStream.isWaiting()) {
247
this.sockets[rChannelId].bufferedStream.emit('readable');
248
}
249
}
265
- else
266
- {
250
+ else {
251
//console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_WINDOW_ADJUST');
252
}
253
break;
@@ -271,11 +255,9 @@ function lme_heci()
255
var rChannelId = chunk.readUInt32BE(1);
256
var dataLen = chunk.readUInt32BE(5);
257
var data = chunk.slice(9, 9 + dataLen);
274
- if (this.sockets[rChannelId] != undefined)
275
- {
258
+ if (this.sockets[rChannelId] != undefined) {
259
this.sockets[rChannelId].pendingBytes.push(data.length);
277
- this.sockets[rChannelId].write(data, function ()
278
- {
260
+ this.sockets[rChannelId].write(data, function () {
261
var written = this.pendingBytes.shift();
262
var outBuffer = Buffer.alloc(9);
263
outBuffer.writeUInt8(APF_CHANNEL_WINDOW_ADJUST, 0);
@@ -284,35 +266,31 @@ function lme_heci()
266
this.HECI.write(outBuffer);
267
});
268
}
287
- else
288
- {
269
+ else {
270
//console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_DATA');
271
}
272
break;
273
case APF_CHANNEL_CLOSE:
274
var rChannelId = chunk.readUInt32BE(1);
294
- if (this.sockets[rChannelId] != undefined)
295
- {
296
- this.sockets[rChannelId].end();
275
+ if (this.sockets[rChannelId] != undefined) {
276
+ this.sockets[rChannelId].end();
277
var amtId = this.sockets[rChannelId].lme.amtId;
278
var buffer = Buffer.alloc(5);
279
delete this.sockets[rChannelId];
300
-
280
+
281
buffer.writeUInt8(APF_CHANNEL_CLOSE, 0);
282
buffer.writeUInt32BE(amtId, 1);
283
this.write(buffer);
284
}
305
- else
306
- {
285
+ else {
286
//console.log('Unknown Recipient ID/' + rChannelId + ' for APF_CHANNEL_CLOSE');
287
}
288
break;
289
}
290
});
291
});
313
-
314
- this.bindDuplexStream = function (duplexStream, remoteFamily, localPort)
315
- {
292
+
293
+ this.bindDuplexStream = function (duplexStream, remoteFamily, localPort) {
294
var socket = duplexStream;
295
//console.log('New [' + remoteFamily + '] Virtual Connection/' + socket.localPort);
296
socket.pendingBytes = [];
@@ -327,19 +305,16 @@ function lme_heci()
305
buffer.writeUInt32BE(socket.lme.ourId);
306
buffer.writeUInt32BE(this.INITIAL_RXWINDOW_SIZE);
307
buffer.writeUInt32BE(0xFFFFFFFF);
330
- for (var i = 0; i < 2; ++i)
331
- {
332
- if (remoteFamily == 'IPv6')
333
- {
308
+ for (var i = 0; i < 2; ++i) {
309
+ if (remoteFamily == 'IPv6') {
310
buffer.writeUInt32BE(3);
311
buffer.write('::1');
312
}
337
- else
338
- {
313
+ else {
314
buffer.writeUInt32BE(9);
315
buffer.write('127.0.0.1');
316
}
342
-
317
+
318
buffer.writeUInt32BE(localPort);
319
}
320
this._LME.write(buffer.buffer);
@@ -347,7 +322,7 @@ function lme_heci()
322
this._LME.sockets[socket.lme.ourId] = socket;
323
socket.pause();
324
};
350
-
325
+
326
this._LME.connect(heci.GUIDS.LME, { noPipeline: 0 });
327
}
328
meshagent.js
+4
-3
@@ -148,10 +148,11 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
148
}
149
}
150
else if (cmdid == 15) { // MeshCommand_AgentTag
151
- ChangeAgentTag(msg.substring(2));
151
+ var tag = msg.substring(2);
152
+ while (tag.charCodeAt(tag.length - 1) == 0) { tag = tag.substring(0, tag.length - 1); } // Remove end-of-line zeros.
153
+ ChangeAgentTag(tag);
154
}
153
- }
154
- else if (obj.authenticated < 2) { // We are not authenticated
155
+ } else if (obj.authenticated < 2) { // We are not authenticated
156
var cmd = obj.common.ReadShort(msg, 0);
157
if (cmd == 1) {
158
// Agent authentication request
meshmail.js
+24
-6
@@ -26,6 +26,14 @@ module.exports.CreateMeshMain = function (parent) {
26
var accountResetMailHtml = '<div style="font-family:Arial,Helvetica,sans-serif"><table style="background-color:#003366;color:lightgray;width:100%" cellpadding=8><tr><td><b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Verification</b></td></tr></table><p>Hi [[[USERNAME]]], <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> is requesting an account password reset, click on the following link to complete the process.</p><p style="margin-left:30px"><a href="[[[CALLBACKURL]]]">Click here to reset your account password.</a></p>If you did not initiate this request, please ignore this mail.</div>';
27
var accountResetMailText = '[[[SERVERNAME]]] - Account Reset\r\n\r\nHi [[[USERNAME]]], [[[SERVERNAME]]] ([[[SERVERURL]]]) is requesting an account password reset. Nagivate to the following link to complete the process: [[[CALLBACKURL]]]\r\nIf you did not initiate this request, please ignore this mail.\r\n';
28
29
+ // Default account invite mail
30
+ var accountInviteSubject = '[[[SERVERNAME]]] - Agent Installation Invitation';
31
+ var accountInviteMailHtml = '<div style="font-family:Arial,Helvetica,sans-serif"><table style="background-color:#003366;color:lightgray;width:100%" cellpadding=8><tr><td><b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Agent Installation</b></td></tr></table><p>User [[[USERNAME]]] on server <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> is requesting that you install a remote management agent. WARNING: this will allow the requester to <u>take control of your computer</u>. If you wish to do this, click on the following link to download the agent.</p><p style="margin-left:30px"><a href="[[[CALLBACKURL]]]">Click here to download the MeshAgent for Windows.</a></p>If you did not know about this request, please ignore this mail.</div>';
32
+ var accountInviteMailText = '[[[SERVERNAME]]] - Agent Installation Invitation\r\n\r\nUser [[[USERNAME]]] on server [[[SERVERNAME]]] ([[[SERVERURL]]]) is requesting you install a remote management agent. WARNING: This will allow the requester to take control of your computer. If you wish to do this, click on the following link to download the agent: [[[CALLBACKURL]]]\r\nIf you do not know about this request, please ignore this mail.\r\n';
33
+
34
+ function EscapeHtml(x) { if (typeof x == "string") return x.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<').replace(/"/g, '"').replace(/'/g, '''); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
35
+ function EscapeHtmlBreaks(x) { if (typeof x == "string") return x.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<').replace(/"/g, '"').replace(/'/g, ''').replace(/\r/g, '<br />').replace(/\n/g, '').replace(/\t/g, ' '); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
36
+
37
// Setup mail server
38
var options = { host: parent.config.smtp.host, secure: (parent.config.smtp.tls == true), tls: { rejectUnauthorized: false } };
39
if (parent.config.smtp.port != null) { options.port = parent.config.smtp.port; }
@@ -33,7 +41,7 @@ module.exports.CreateMeshMain = function (parent) {
41
obj.smtpServer = nodemailer.createTransport(options);
42
43
// Perform all e-mail substitution
36
- function mailReplacements(text, domain, username, email, cookie) {
44
+ function mailReplacements(text, domain, username, email, options) {
45
var url;
46
if (domain.dns == null) {
47
// Default domain or subdomain of the default.
@@ -42,7 +50,10 @@ module.exports.CreateMeshMain = function (parent) {
50
// Domain with a DNS name.
51
url = 'http' + ((obj.parent.args.notls == null) ? 's' : '') + '://' + domain.dns + ':' + obj.parent.args.port + domain.url;
52
}
45
- if (cookie != null) { text = text.split('[[[CALLBACKURL]]]').join(url + 'checkmail?c=' + cookie) }
53
+ if (options) {
54
+ if (options.cookie != null) { text = text.split('[[[CALLBACKURL]]]').join(url + 'checkmail?c=' + options.cookie) }
55
+ if (options.meshid != null) { text = text.split('[[[CALLBACKURL]]]').join(url + 'meshagents?id=3&meshid=' + options.meshid.split('/')[2] + '&tag=mailto:' + EscapeHtml(email)) }
56
+ }
57
return text.split('[[[USERNAME]]]').join(username).split('[[[SERVERURL]]]').join(url).split('[[[SERVERNAME]]]').join(domain.title);
58
}
59
@@ -54,17 +65,24 @@ module.exports.CreateMeshMain = function (parent) {
65
66
// Send account check mail
67
obj.sendAccountCheckMail = function (domain, username, email) {
57
- if ((parent.certificates == null) || (parent.certificates.CommonName == null)) return; // If the server name is not set, no reset possible.
68
+ if ((parent.certificates == null) || (parent.certificates.CommonName == null) || (parent.certificates.CommonName == 'un-configured')) return; // If the server name is not set, no reset possible.
69
var cookie = obj.parent.encodeCookie({ u: domain.id + '/' + username, e: email, a: 1 }, obj.mailCookieEncryptionKey);
59
- obj.pendingMails.push({ to: email, from: parent.config.smtp.from, subject: mailReplacements(accountCheckSubject, domain, username, email), text: mailReplacements(accountCheckMailText, domain, username, email, cookie), html: mailReplacements(accountCheckMailHtml, domain, username, email, cookie) });
70
+ obj.pendingMails.push({ to: email, from: parent.config.smtp.from, subject: mailReplacements(accountCheckSubject, domain, username, email), text: mailReplacements(accountCheckMailText, domain, username, email, { cookie: cookie }), html: mailReplacements(accountCheckMailHtml, domain, username, email, { cookie: cookie }) });
71
sendNextMail();
72
}
73
74
// Send account reset mail
75
obj.sendAccountResetMail = function (domain, username, email) {
65
- if ((parent.certificates == null) || (parent.certificates.CommonName == null)) return; // If the server name is not set, don't validate the email address.
76
+ if ((parent.certificates == null) || (parent.certificates.CommonName == null) || (parent.certificates.CommonName == 'un-configured')) return; // If the server name is not set, don't validate the email address.
77
var cookie = obj.parent.encodeCookie({ u: domain.id + '/' + username, e: email, a: 2 }, obj.mailCookieEncryptionKey);
67
- obj.pendingMails.push({ to: email, from: parent.config.smtp.from, subject: mailReplacements(accountResetSubject, domain, username, email), text: mailReplacements(accountResetMailText, domain, username, email, cookie), html: mailReplacements(accountResetMailHtml, domain, username, email, cookie) });
78
+ obj.pendingMails.push({ to: email, from: parent.config.smtp.from, subject: mailReplacements(accountResetSubject, domain, username, email), text: mailReplacements(accountResetMailText, domain, username, email, { cookie: cookie }), html: mailReplacements(accountResetMailHtml, domain, username, email, { cookie: cookie }) });
79
+ sendNextMail();
80
+ }
81
+
82
+ // Send agent invite mail
83
+ obj.sendAgentInviteMail = function (domain, username, email, meshid) {
84
+ if ((parent.certificates == null) || (parent.certificates.CommonName == null) || (parent.certificates.CommonName == 'un-configured')) return; // If the server name is not set, can't do this.
85
+ obj.pendingMails.push({ to: email, from: parent.config.smtp.from, subject: mailReplacements(accountInviteSubject, domain, username, email), text: mailReplacements(accountInviteMailText, domain, username, email, { meshid: meshid }), html: mailReplacements(accountInviteMailHtml, domain, username, email, { meshid: meshid }) });
86
sendNextMail();
87
}
88
meshuser.js
+20
-1
@@ -427,7 +427,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
427
if ((command.meshtype == 1) || (command.meshtype == 2)) {
428
// Create a type 1 agent-less Intel AMT mesh.
429
obj.parent.crypto.randomBytes(48, function (err, buf) {
430
- var meshid = 'mesh/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$');;
430
+ var meshid = 'mesh/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
431
var links = {}
432
links[user._id] = { name: user.name, rights: 0xFFFFFFFF };
433
var mesh = { type: 'mesh', _id: meshid, name: command.meshname, mtype: command.meshtype, desc: command.desc, domain: domain.id, links: links };
@@ -876,6 +876,25 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
876
});
877
break;
878
}
879
+ case 'inviteAgent':
880
+ {
881
+ if (obj.parent.parent.mailserver == null) return; // This operation requires the email server
882
+ if ((obj.parent.parent.certificates.CommonName == null) || (obj.parent.parent.certificates.CommonName == 'un-configured')) return; // Server name must be configured
883
+ if ((command.meshid.split('/').length != 3) || (command.meshid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
884
+
885
+ // Get the mesh
886
+ var mesh = obj.parent.meshes[command.meshid];
887
+ if (mesh) {
888
+ if (mesh.mtype != 2) return; // This operation is only allowed for mesh type 2, agent mesh
889
+
890
+ // Check if this user has rights to do this
891
+ //if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 4) == 0)) return;
892
+
893
+ // Perform email invitation
894
+ obj.parent.parent.mailserver.sendAgentInviteMail(domain, user.name, command.email, command.meshid);
895
+ }
896
+ break;
897
+ }
898
}
899
});
900
package.json
+1
-1
@@ -1,6 +1,6 @@
1
{
2
"name": "meshcentral",
3
- "version": "0.1.2-s",
3
+ "version": "0.1.3-c",
4
"keywords": [
5
"Remote Management",
6
"Intel AMT",
views/default.handlebars
+52
-5
@@ -1283,9 +1283,10 @@
1283
}
1284
1285
function ondockeydown(e) {
1286
- if (!xxdialogMode && xxcurrentView == 11 && desktop) return desktop.m.handleKeyDown(e);
1287
- if (!xxdialogMode && xxcurrentView == 12 && terminal && terminal.State == 3) return terminal.m.TermHandleKeyDown(e);
1286
+ if (!xxdialogMode && xxcurrentView == 11 && desktop) { return desktop.m.handleKeyDown(e); }
1287
+ if (!xxdialogMode && xxcurrentView == 12 && terminal && terminal.State == 3) { return terminal.m.TermHandleKeyDown(e); }
1288
if (!xxdialogMode && xxcurrentView == 13 && e.keyCode == 116 && p13filetree != null) { haltEvent(e); return false; } // F5 Refresh on files
1289
+ if (!xxdialogMode && xxcurrentView == 15) { return agentConsoleHandleKeys(e); }
1290
if (xxdialogMode || xxcurrentView != 1 || e.ctrlKey == true || e.altKey == true || e.metaKey == true) return;
1291
var processed = 0;
1292
if (Q('viewselect').value < 3) {
@@ -1460,6 +1461,9 @@
1461
}
1462
if (mesh.mtype == 2) {
1463
r += ' <a style=cursor:pointer;font-size:10px title="Add a new computer to this mesh by installing the mesh agent." onclick=addAgentToMesh(\"' + mesh._id + '\")>Add Agent</a>';
1464
+ if (features & 64) {
1465
+ r += ' <a style=cursor:pointer;font-size:10px title="Invite someone to install the mesh agent." onclick=inviteAgentToMesh(\"' + mesh._id + '\")>Invite</a>';
1466
+ }
1467
}
1468
return r;
1469
}
@@ -1568,6 +1572,33 @@
1572
QV('dlgAddCira2', val == 2);
1573
}
1574
1575
+ // Return true is the input string looks like an email address
1576
+ function checkEmail(str) {
1577
+ var x = str.split('@');
1578
+ var ok = ((x.length == 2) && (x[0].length > 0) && (x[1].split('.').length > 1) && (x[1].length > 2));
1579
+ if (ok == true) { var y = x[1].split('.'); for (var i in y) { if (y[i].length == 0) { ok = false; } } }
1580
+ return ok;
1581
+ }
1582
+
1583
+ function inviteAgentToMesh(meshid) {
1584
+ if (xxdialogMode) return;
1585
+ var mesh = meshes[meshid];
1586
+ var meshidx = meshid.substring(5);
1587
+ if (meshidx[0] == '/') meshidx = meshidx.substring(1);
1588
+ var x = "Invite someone to install the mesh agent. An email with be sent with the link to the mesh agent installation for " + EscapeHtml(mesh.name) + ".<br /><br />";
1589
+ x += addHtmlValue('E-Mail', '<input id=agentInviteEmail style=width:240px onkeyup=validateAgentInvite()></input>');
1590
+ setDialogMode(2, "Invite Mesh Agent", 3, performAgentInvite, x, meshid);
1591
+ validateAgentInvite();
1592
+ }
1593
+
1594
+ function validateAgentInvite() {
1595
+ QE('idx_dlgOkButton', checkEmail(Q('agentInviteEmail').value));
1596
+ }
1597
+
1598
+ function performAgentInvite(button, meshid) {
1599
+ meshserver.Send({ action: 'inviteAgent', meshid: meshid, email: Q('agentInviteEmail').value });
1600
+ }
1601
+
1602
function addAgentToMesh(meshid) {
1603
if (xxdialogMode) return;
1604
var mesh = meshes[meshid];
@@ -1580,7 +1611,7 @@
1611
//x += "<div id=agins_windows>To add a new computer to mesh " + EscapeHtml(mesh.name) + ", download the mesh agent and configuration file and install the agent on the computer to manage.<br /><br />";
1612
x += "<div id=agins_windows>To add a new computer to mesh " + EscapeHtml(mesh.name) + ", download the mesh agent and install it the computer to manage. This agent has server and mesh information embedded within it.<br /><br />";
1613
x += addHtmlValue('Mesh Agent', '<a href="meshagents?id=3&meshid=' + meshid.split('/')[2] + '" target="_blank">Windows executable (.exe)</a>');
1583
- //x += addHtmlValue('Settings File', '<a href="meshsettings?id=' + meshid.split('/')[2] + '" target="_blank">' + EscapeHtml(mesh.name) + ' settings (.msh)</a>');
1614
+ if (debugmode == true) { x += addHtmlValue('Settings File', '<a href="meshsettings?id=' + meshid.split('/')[2] + '" target="_blank">' + EscapeHtml(mesh.name) + ' settings (.msh)</a>'); }
1615
x += "</div>";
1616
1617
// Linux agent install
@@ -2491,7 +2522,9 @@
2522
2523
// Attribute: Mesh Agent Tag
2524
if ((node.agent != null) && (node.agent.tag != null)) {
2494
- x += addDeviceAttribute('Agent Tag', node.agent.tag);
2525
+ var tag = EscapeHtml(node.agent.tag);
2526
+ if (tag.startsWith('mailto:')) { tag = '<a href="' + tag + '">' + tag.substring(7) + '</a>'; }
2527
+ x += addDeviceAttribute('Agent Tag', tag);
2528
}
2529
2530
// Attribute: Intel AMT
@@ -3664,7 +3697,6 @@
3697
if (e.keyCode == 13 && consoleFocus == 0) { p15consoleSend(e); processed = 1; }
3698
else if (e.keyCode == 8 && consoleFocus == 0) { var x = box.value; box.value = x.substring(0, x.length - 1); processed = 1; }
3699
else if (e.keyCode == 27) { box.value = ''; processed = 1; }
3667
- else if (e.key.length === 1 && consoleFocus == 0) { box.value = ((box.value + e.key)); processed = 1; }
3700
else if ((e.keyCode == 38) || (e.keyCode == 40)) { // Arrow up || Arrow down
3701
var hindex = consoleHistory.indexOf(box.value);
3702
//console.log(hindex, consoleHistory);
@@ -3673,12 +3705,27 @@
3705
else if ((e.keyCode == 40) && (hindex == 0)) { box.value = ''; }
3706
processed = 1;
3707
}
3708
+ else if (e.key.length === 1) {
3709
+ //box.value = ((box.value + e.key));
3710
+ insertTextAtCursor(box, e.key);
3711
+ processed = 1;
3712
+ }
3713
} else {
3714
if (e.charCode != 0 && consoleFocus == 0) { box.value = ((box.value + String.fromCharCode(e.charCode))); processed = 1; }
3715
}
3716
if (processed > 0) { return haltEvent(e); }
3717
}
3718
3719
+ // Insert text at the cursor location on the
3720
+ function insertTextAtCursor(ctrl, val) {
3721
+ if (document.selection) { ctrl.focus(); sel = document.selection.createRange(); sel.text = val; }
3722
+ else if (ctrl.selectionStart || ctrl.selectionStart == '0') {
3723
+ var start = ctrl.selectionStart, end = ctrl.selectionEnd;
3724
+ ctrl.value = ctrl.value.substring(0, start) + val + ctrl.value.substring(end, ctrl.value.length);
3725
+ ctrl.setSelectionRange(end + 1, end + 1);
3726
+ } else { ctrl.value += myValue; }
3727
+ }
3728
+
3729
var consoleNode;
3730
function setupConsole() {
3731
// Setup the console
webserver.js
+6
-1
@@ -675,6 +675,8 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
675
user = obj.users[req.session.userid]
676
logoutcontrol = 'Welcome ' + user.name + '.';
677
}
678
+
679
+ // Give the web page a list of supported server features
680
var features = 0;
681
if (obj.args.wanonly == true) { features += 1; } // WAN-only mode
682
if (obj.args.lanonly == true) { features += 2; } // LAN-only mode
@@ -682,6 +684,9 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
684
if (domain.userQuota == -1) { features += 8; } // No server files mode
685
if (obj.args.tlsoffload == true) { features += 16; } // No mutual-auth CIRA
686
if ((parent.config != null) && (parent.config.settings != null) && (parent.config.settings.allowframing == true)) { features += 32; } // Allow site within iframe
687
+ if ((obj.parent.mailserver != null) && (obj.parent.certificates.CommonName != null) && (obj.parent.certificates.CommonName != 'un-configured')) { features += 64; } // Email invites
688
+
689
+ // Send the master web application
690
if ((!obj.args.user) && (obj.args.nousers != true) && (nologout == false)) { logoutcontrol += ' <a href=' + domain.url + 'logout?' + Math.random() + ' style=color:white>Logout</a>'; } // If a default user is in use or no user mode, don't display the logout button
691
res.render(obj.path.join(__dirname, 'views/default'), { viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: args.port, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, mpspass: args.mpspass, webcerthash: obj.webCertificateHashBase64, footer: (domain.footer == null) ? '' : domain.footer });
692
} else {
@@ -1426,7 +1431,7 @@ module.exports.CreateWebServer = function (parent, db, args, secret, certificate
1431
if (xdomain != '') xdomain += "/";
1432
var meshsettings = "MeshName=" + mesh.name + "\r\nMeshType=" + mesh.mtype + "\r\nMeshID=0x" + meshidhex + "\r\nServerID=" + serveridhex + "\r\n";
1433
if (obj.args.lanonly != true) { meshsettings += "MeshServer=ws" + (obj.args.notls ? '' : 's') + "://" + getWebServerName(domain) + ":" + obj.args.port + "/" + xdomain + "agent.ashx\r\n"; } else { meshsettings += "MeshServer=local"; }
1429
-
1434
+ if (req.query.tag != true) { meshsettings += "Tag=" + req.query.tag + "\r\n"; }
1435
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=' + argentInfo.rname });
1436
obj.parent.exeHandler.streamExeWithMeshPolicy({ platform: 'win32', sourceFileName: obj.parent.meshAgentBinaries[req.query.id].path, destinationStream: res, msh: meshsettings, peinfo: obj.parent.meshAgentBinaries[req.query.id].pe });
1437
});