Revert "Remove code redundancy"

Ylian Saint-Hilaire committed May 25, 2023 at 10:18 UTC 9d322746e60a557006ed3c7c3552091ae20e7665
2 files changed +18399 -18367
public/styles/style.css
+40 -8
@@ -1533,44 +1533,76 @@ a {
1533 opacity: 1;
1534 }
1535
1536 -/*groups all buttons repeated code into one block*/
1537 -.lbtg{
1536 +.lb1 {
1537 + background: url(../images/leftbar-64.png) -1px -2px;
1538 + background-image: image-set(url(../images/leftbar-64.png) 1x, url(../images/leftbar-128.png) 2x);
1539 height: 62px;
1540 width: 62px;
1541 cursor: pointer;
1542 border: none;
1543 position: absolute;
1544 top: 6px;
1544 - left: 6px;
1545 -}
1546 -.lb1 {
1547 - background: url(../images/leftbar-64.png) -1px -2px;
1548 - background-image: image-set(url(../images/leftbar-64.png) 1x, url(../images/leftbar-128.png) 2x);
1545 + left: 6px;
1546 }
1547
1548 .lb2 {
1549 background: url(../images/leftbar-64.png) -66px -2px;
1550 background-image: image-set(url(../images/leftbar-64.png) 1x, url(../images/leftbar-128.png) 2x);
1551 + height: 62px;
1552 + width: 62px;
1553 + cursor: pointer;
1554 + border: none;
1555 + position: absolute;
1556 + top: 6px;
1557 + left: 6px;
1558 }
1559
1560 .lb3 {
1561 background: url(../images/leftbar-64.png) -130px -2px;
1562 background-image: image-set(url(../images/leftbar-64.png) 1x, url(../images/leftbar-128.png) 2x);
1563 + height: 62px;
1564 + width: 62px;
1565 + cursor: pointer;
1566 + border: none;
1567 + position: absolute;
1568 + top: 6px;
1569 + left: 6px;
1570 }
1571
1572 .lb4 {
1573 background: url(../images/leftbar-64.png) -194px -2px;
1574 background-image: image-set(url(../images/leftbar-64.png) 1x, url(../images/leftbar-128.png) 2x);
1575 + height: 62px;
1576 + width: 62px;
1577 + cursor: pointer;
1578 + border: none;
1579 + position: absolute;
1580 + top: 6px;
1581 + left: 6px;
1582 }
1583
1584 .lb5 {
1585 background: url(../images/leftbar-64.png) -257px -2px;
1586 background-image: image-set(url(../images/leftbar-64.png) 1x, url(../images/leftbar-128.png) 2x);
1587 + height: 62px;
1588 + width: 62px;
1589 + cursor: pointer;
1590 + border: none;
1591 + position: absolute;
1592 + top: 6px;
1593 + left: 6px;
1594 }
1595
1596 .lb6 {
1597 background: url(../images/leftbar-64.png) -318px -2px;
1598 background-image: image-set(url(../images/leftbar-64.png) 1x, url(../images/leftbar-128.png) 2x);
1599 + height: 62px;
1600 + width: 62px;
1601 + cursor: pointer;
1602 + border: none;
1603 + position: absolute;
1604 + top: 6px;
1605 + left: 6px;
1606 }
1607
1608 .m0 {
@@ -3246,4 +3278,4 @@ a {
3278 padding: 6px 12px;
3279 border: 1px solid #ccc;
3280 border-top: none;
3249 -}
3281 +}
\ No newline at end of file
views/default.handlebars
+18359 -18359
@@ -1,18359 +1,18359 @@
1 -<!DOCTYPE html>
2 -<html lang="en" dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
3 -<head>
4 - <meta http-equiv="X-UA-Compatible" content="IE=edge" />
5 - <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
6 - <meta name="viewport" content="user-scalable=1.0,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0" />
7 - <meta name="apple-mobile-web-app-capable" content="yes" />
8 - <meta name="format-detection" content="telephone=no" />
9 - <meta name="robots" content="noindex,nofollow">
10 - <link type="image/x-icon" href="{{{domainurl}}}favicon.ico" rel="shortcut icon" />
11 - <link keeplink=1 type="text/css" href="styles/style.css" media="screen" rel="stylesheet" title="CSS" />
12 - <link type="text/css" href="styles/ol.css" media="screen" rel="stylesheet" title="CSS" />
13 - <link type="text/css" href="styles/ol3-contextmenu.min.css" media="screen" rel="stylesheet" title="CSS" />
14 - <link type="text/css" href="styles/xterm.css" media="screen" rel="stylesheet" title="CSS" />
15 - <link type="text/css" href="styles/flatpickr.min.css" media="screen" rel="stylesheet" title="CSS">
16 - <link rel="apple-touch-icon" href="/favicon-303x303.png" />
17 - <script type="text/javascript" src="scripts/common-0.0.1{{{min}}}.js"></script>
18 - <script type="text/javascript" src="scripts/meshcentral{{{min}}}.js"></script>
19 - <script type="text/javascript" src="scripts/amt-0.2.0{{{min}}}.js"></script>
20 - <script type="text/javascript" src="scripts/amt-wsman-0.2.0{{{min}}}.js"></script>
21 - <script type="text/javascript" src="scripts/amt-desktop-0.0.2{{{min}}}.js"></script>
22 - <script type="text/javascript" src="scripts/amt-terminal-0.0.2{{{min}}}.js"></script>
23 - <script type="text/javascript" src="scripts/zlib{{{min}}}.js"></script>
24 - <script type="text/javascript" src="scripts/zlib-inflate{{{min}}}.js"></script>
25 - <script type="text/javascript" src="scripts/zlib-adler32{{{min}}}.js"></script>
26 - <script type="text/javascript" src="scripts/zlib-crc32{{{min}}}.js"></script>
27 - <script type="text/javascript" src="scripts/amt-redir-ws-0.1.0{{{min}}}.js"></script>
28 - <script type="text/javascript" src="scripts/amt-wsman-ws-0.2.0{{{min}}}.js"></script>
29 - <script type="text/javascript" src="scripts/agent-redir-ws-0.1.1{{{min}}}.js"></script>
30 - <script type="text/javascript" src="scripts/agent-redir-rtc-0.1.0{{{min}}}.js"></script>
31 - <script type="text/javascript" src="scripts/agent-desktop-0.0.2{{{min}}}.js"></script>
32 - <script type="text/javascript" src="scripts/agent-rdp-0.0.1{{{min}}}.js"></script>
33 - <script type="text/javascript" src="scripts/qrcode.min.js"></script>
34 - <script type="text/javascript" src="scripts/xterm{{{min}}}.js"></script>
35 - <script type="text/javascript" src="scripts/xterm-addon-fit{{{min}}}.js"></script>
36 - <script type="text/javascript" src="scripts/flatpickr.js"></script>
37 - <script type="text/javascript" src="mstsc/mstsc.js"></script>
38 - <script type="text/javascript" src="mstsc/keyboard.js"></script>
39 - <script type="text/javascript" src="mstsc/rle.js"></script>
40 - <script type="text/javascript" src="mstsc/client.js"></script>
41 - <script type="text/javascript" src="mstsc/canvas.js"></script>
42 - <script keeplink=1 type="text/javascript" src="scripts/u2f-api{{{min}}}.js"></script>
43 - <script keeplink=1 type="text/javascript" src="scripts/charts{{{min}}}.js"></script>
44 - <script keeplink=1 type="text/javascript" src="scripts/filesaver.min.js"></script>
45 - <script keeplink=1 type="text/javascript" src="scripts/ol{{{min}}}.js"></script>
46 - <script keeplink=1 type="text/javascript" src="scripts/ol3-contextmenu{{{min}}}.js"></script>
47 - <title>{{{title}}}</title>
48 -</head>
49 -<body id="body" oncontextmenu="handleContextMenu(event)" style="display:none;min-width:495px" onload="if (typeof(startup) !== 'undefined') startup();">
50 - <!-- right click menu -->
51 - <div id="contextMenu" class="contextMenu noselect" style="display:none">
52 - <div id="cxinfo" class="cmtext" onclick="cmaction(1,event)"><b>Information</b></div>
53 - <div id="cxdesktop" class="cmtext" onclick="cmaction(3,event)">Desktop</div>
54 - <div id="cxterminal" class="cmtext" onclick="cmaction(2,event)">Terminal</div>
55 - <div id="cxfiles" class="cmtext" onclick="cmaction(4,event)">Files</div>
56 - <div id="cxevents" class="cmtext" onclick="cmaction(5,event)">Events</div>
57 - <div id="cxdetails" class="cmtext" onclick="cmaction(6,event)">Details</div>
58 - <div id="cxconsole" class="cmtext" onclick="cmaction(7,event)">Console</div>
59 - <div id="cxwebrdp" class="cmtext" onclick="cmaction(11,event)">Web-RDP</div>
60 - <div id="cxwebvnc" class="cmtext" onclick="cmaction(12,event)">Web-VNC</div>
61 - <div id="cxwebssh" class="cmtext" onclick="cmaction(13,event)">Web-SSH</div>
62 - <div id="cxplugins" class="cmtext" onclick="cmaction(8,event)" style="display:none">Plugins</div>
63 - <hr id="cxmgroupsplit" />
64 - <div id="cxstar" class="cmtext" onclick="cmaction(10,event)" style="display:none">Toggle Star</div>
65 - <div id="cxmdesktop" class="cmtext" onclick="cmaction(9,event)" style="display:none">Multi-Desktop</div>
66 - </div>
67 - <div id="meshContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
68 - <div id="cxselectall" class="cmtext" onclick="cmmeshaction(1,event)">Select All</div>
69 - <div id="cxselectnone" class="cmtext" onclick="cmmeshaction(2,event)">Select None</div>
70 - <!--
71 - <hr id="cxmgroupsplit2" style="display:none" />
72 - <div id="cxmmdesktop" class="cmtext" style="display:none" onclick="cmmeshaction(3,event)">Multi-Desktop</div>
73 - -->
74 - </div>
75 - <div id="filesShellContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
76 - <div class="cmtext" onclick="cmconnectfilesaction()">Ask Consent</div>
77 - </div>
78 - <div id="termShellContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
79 - <div class="cmtext" onclick="cmtermaction(1,0,event)"><b>Admin Shell</b></div>
80 - <div class="cmtext" onclick="cmtermaction(6,0,event)">Admin PowerShell</div>
81 - <div class="cmtext" onclick="cmtermaction(8,0,event)">User Shell</div>
82 - <div class="cmtext" onclick="cmtermaction(9,0,event)">User PowerShell</div>
83 - <div class="cmtext" onclick="cmtermaction(1,0x10,event)">Ask Admin Shell</div>
84 - <div class="cmtext" onclick="cmtermaction(6,0x10,event)">Ask Admin PowerShell</div>
85 - <div class="cmtext" onclick="cmtermaction(8,0x10,event)">Ask User Shell</div>
86 - <div class="cmtext" onclick="cmtermaction(9,0x10,event)">Ask User PowerShell</div>
87 - </div>
88 - <div id="termShellContextMenu2" class="contextMenu noselect" style="display:none;min-width:0px">
89 - <div class="cmtext" onclick="cmtermaction(8,0,event)"><b>User Shell</b></div>
90 - <div class="cmtext" onclick="cmtermaction(9,0,event)">User PowerShell</div>
91 - </div>
92 - <div id="termShellContextMenuLinux" class="contextMenu noselect" style="display:none;min-width:0px">
93 - <div class="cmtext" onclick="cmtermaction(1,0,event)"><b>Root Shell</b></div>
94 - <div class="cmtext" onclick="cmtermaction(8,0,event)">User Shell</div>
95 - <div class="cmtext" onclick="cmtermaction(100,0,event)">Login Shell</div>
96 - </div>
97 - <div id="deskConnectContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
98 - <div class="cmtext" onclick="cmdeskaction(1,event)">Ask Consent + Bar</div>
99 - <div class="cmtext" onclick="cmdeskaction(2,event)">Ask Consent</div>
100 - <div class="cmtext" onclick="cmdeskaction(3,event)">Privacy Bar</div>
101 - </div>
102 - <div id="deskDisconnectContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
103 - <div class="cmtext" onclick="cmdeskaction(10,event)">Disconnect and Lock</div>
104 - <div class="cmtext" onclick="cmdeskaction(11,event)">Disconnect</div>
105 - </div>
106 - <div id="altPortContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
107 - <div class="cmtext" onclick="cmaltportaction(1,event)">Alternate Port</div>
108 - </div>
109 - <div id="sshPortContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
110 - <div class="cmtext" onclick="cmsshportaction(1,event)">Alternate Port</div>
111 - </div>
112 - <div id="rfbPortContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
113 - <div class="cmtext" onclick="cmrfbportaction(1,event)">Alternate Port</div>
114 - </div>
115 - <div id="httpPortContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
116 - <div class="cmtext" onclick="cmhttpportaction(1,event)">Alternate Port</div>
117 - </div>
118 - <div id="httpsPortContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
119 - <div class="cmtext" onclick="cmhttpsportaction(1,event)">Alternate Port</div>
120 - </div>
121 - <div id="filesContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
122 - <div class="cmtext" onclick="cmfilesaction(1,event)">Rename</div>
123 - <div class="cmtext" onclick="cmfilesaction(2,event)">Edit</div>
124 - <div class="cmtext" onclick="cmfilesaction(3,event)">Delete</div>
125 - </div>
126 - <div id="expandAllContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
127 - <div class="cmtext" onclick="cmexpandaction(1,event)">Expand All</div>
128 - <div class="cmtext" onclick="cmexpandaction(2,event)">Collapse All</div>
129 - </div>
130 - <div id="deskPlayerContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
131 - <div class="cmtext" onclick="cmdeskplayeraction(1,event)">Open Player...</div>
132 - </div>
133 - <div id="deskKeyShortcutContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
134 - <div class="cmtext" onclick="cmdeskshortcutaction(1,event)">Customize...</div>
135 - </div>
136 - <div id="deskPreConfigShortcutContextMenu" class="contextMenu noselect" style="display:none;min-width:0px;max-height:calc(80% - 50px);overflow-y:auto">
137 - <span id="deskPreConfigShortcutContextMenu1"></span>
138 - <span id="deskPreConfigShortcutContextMenu2"></span>
139 - <div class="cmtext" onclick="cmdeskpreconfigtypeaction(-1,event)">Customize...</div>
140 - </div>
141 - <div id="deskPreConfigScriptContextMenu" class="contextMenu noselect" style="display:none;min-width:0px;max-height:calc(80% - 50px);overflow-y:auto">
142 - <span id="deskPreConfigScriptContextMenu1"></span>
143 - <span id="deskPreConfigScriptContextMenu2"></span>
144 - </div>
145 - <!--
146 - <div id="pluginTabContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
147 - <div id="cxclose" class="cmtext" onclick="pluginTabClose(event)">Close Tab</div>
148 - </div>
149 - -->
150 - <!-- main page -->
151 - <div id=container>
152 - <div id="notifiyBox" class="notifiyBox" style="display:none"></div>
153 - <div id=masthead class=noselect>
154 - <div style="float:left">{{{titlehtml}}}</div>
155 - <div class="title">{{{title1}}}</div>
156 - <div class="title2">{{{title2}}}</div>
157 - <div style="float:right">
158 - <div id=notificationCount onclick="clickNotificationIcon()" class="unselectable" style="display: none;" title="Click to view current notifications">0</div>
159 - </div>
160 - <p id="logoutControl"><span id=logoutControlSpan class="logoncontrolspan"></span><span id=idleTimeoutNotify style="color:yellow"></span></p>
161 - </div>
162 - <div id="page_leftbar">
163 - <div style="height:16px"></div>
164 - <div id=LeftMenuMyDevices tabindex=0 class="lbbutton lbbuttonsel" title="My Devices" onclick=go(1,event) onkeypress="if (event.key=='Enter') { go(1); }">
165 - <div class="lb2 lbtng"></div>
166 - </div>
167 - <div id=LeftMenuMyAccount tabindex=0 class="lbbutton" title="My Account" onclick=go(2,event) onkeypress="if (event.key=='Enter') { go(2); }">
168 - <div class="lb1 lbtng"></div>
169 - </div>
170 - <div id=LeftMenuMyEvents tabindex=0 class="lbbutton" title="My Events" onclick=go(3,event) onkeypress="if (event.key=='Enter') { go(3); }">
171 - <div class="lb3 lbtng"></div>
172 - </div>
173 - <div id=LeftMenuMyFiles tabindex=0 class="lbbutton" style="display:none" title="My Files" onclick=go(5,event) onkeypress="if (event.key=='Enter') { go(5); }">
174 - <div class="lb4 lbtng"></div>
175 - </div>
176 - <div id=LeftMenuMyUsers tabindex=0 class="lbbutton" style="display:none" title="My Users" onclick=go(4,event) onkeypress="if (event.key=='Enter') { go(4); }">
177 - <div class="lb5 lbtng"></div>
178 - </div>
179 - <div id=LeftMenuMyServer tabindex=0 class="lbbutton" style="display:none" title="My Server" onclick=go(6,event) onkeypress="if (event.key=='Enter') { go(6); }">
180 - <div class="lb6 lbtng"></div>
181 - </div>
182 - </div>
183 - <div id=topbar class=noselect>
184 - <div>
185 - <div style="position:relative">
186 - <span id=logoutControlSpan2></span>
187 - <div tabindex=0 id=uiMenuButton title="User interface selection" onclick="showUserInterfaceSelectMenu()" onkeypress="if (event.key == 'Enter') showUserInterfaceSelectMenu()">
188 - &diams;
189 - <div id=uiMenu style="display:none">
190 - <table>
191 - <tr>
192 - <td>
193 - <div tabindex=0 id=uiViewButton1 class=uiSelector onclick=userInterfaceSelectMenu(1) title="Left bar interface" onkeypress="if (event.key == 'Enter') userInterfaceSelectMenu(1)"><div class="uiSelector1"></div></div>
194 - <div tabindex=0 id=uiViewButton2 class=uiSelector onclick=userInterfaceSelectMenu(2) title="Top bar interface" onkeypress="if (event.key == 'Enter') userInterfaceSelectMenu(2)"><div class="uiSelector2"></div></div>
195 - <div tabindex=0 id=uiViewButton3 class=uiSelector onclick=userInterfaceSelectMenu(3) title="Fixed width interface" onkeypress="if (event.key == 'Enter') userInterfaceSelectMenu(3)"><div class="uiSelector3"></div></div>
196 - </td>
197 - <td>
198 - <div tabindex=0 id=uiViewButton6 class=uiSelector onclick="showNotes(false)" title="Personal Notes" onkeypress="if (event.key == 'Enter') showNotes(false)"><div class="uiSelector6"></div></div>
199 - <div tabindex=0 id=uiViewButton4 class=uiSelector onclick=toggleNightMode() title="Toggle night mode" onkeypress="if (event.key == 'Enter') toggleNightMode()"><div class="uiSelector4"></div></div>
200 - <div tabindex=0 id=uiViewButton5 class=uiSelector onclick=toggleFooterBarMode() title="Toggle footer bar" onkeypress="if (event.key == 'Enter') toggleFooterBarMode()"><div class="uiSelector5"></div></div>
201 - </td>
202 - </tr>
203 - </table>
204 - </div>
205 - </div>
206 - <table id=MainMenuSpan cellpadding=0 cellspacing=0 class=style1>
207 - <tr>
208 - <td tabindex=0 id=MainMenuMyDevices class="topbar_td style3x" onclick=go(1,event) onkeypress="if (event.key == 'Enter') go(1)">My Devices</td>
209 - <td tabindex=0 id=MainMenuMyAccount class="topbar_td style3x" onclick=go(2,event) onkeypress="if (event.key == 'Enter') go(2)">My Account</td>
210 - <td tabindex=0 id=MainMenuMyEvents class="topbar_td style3x" onclick=go(3,event) onkeypress="if (event.key == 'Enter') go(3)">My Events</td>
211 - <td tabindex=0 id=MainMenuMyFiles class="topbar_td style3x" onclick=go(5,event) onkeypress="if (event.key == 'Enter') go(5)">My Files</td>
212 - <td tabindex=0 id=MainMenuMyUsers class="topbar_td style3x" onclick=go(4,event) onkeypress="if (event.key == 'Enter') go(4)">My Users</td>
213 - <td tabindex=0 id=MainMenuMyServer class="topbar_td style3x" onclick=go(6,event) onkeypress="if (event.key == 'Enter') go(6)">My Server</td>
214 - <td class="topbar_td_end style3">&nbsp;</td>
215 - </tr>
216 - </table>
217 - <div id=MainSubMenuSpan style="display:none">
218 - <table id=MainSubMenu cellpadding=0 cellspacing=0 class=style1>
219 - <tr>
220 - <td tabindex=0 id=MainDev class="topbar_td style3x" onclick=go(10,event) onkeypress="if (event.key == 'Enter') go(10)">General</td>
221 - <td tabindex=0 id=MainDevDesktop class="topbar_td style3x" onclick=go(11,event) onkeypress="if (event.key == 'Enter') go(11)">Desktop</td>
222 - <td tabindex=0 id=MainDevTerminal class="topbar_td style3x" onclick=go(12,event) onkeypress="if (event.key == 'Enter') go(12)">Terminal</td>
223 - <td tabindex=0 id=MainDevFiles class="topbar_td style3x" onclick=go(13,event) onkeypress="if (event.key == 'Enter') go(13)">Files</td>
224 - <td tabindex=0 id=MainDevEvents class="topbar_td style3x" onclick=go(16,event) onkeypress="if (event.key == 'Enter') go(16)">Events</td>
225 - <td tabindex=0 id=MainDevInfo class="topbar_td style3x" onclick=go(17,event) onkeypress="if (event.key == 'Enter') go(17)">Details</td>
226 - <td tabindex=0 id=MainDevAmt class="topbar_td style3x" onclick=go(14,event) onkeypress="if (event.key == 'Enter') go(14)">Intel&reg;AMT</td>
227 - <td tabindex=0 id=MainDevConsole class="topbar_td style3x" onclick=go(15,event) onkeypress="if (event.key == 'Enter') go(15)">Console</td>
228 - <td tabindex=0 id=MainDevPlugins class="topbar_td style3x" onclick=go(19,event) onkeypress="if (event.key == 'Enter') go(19)">Plugins</td>
229 - <td class="topbar_td_end style3">&nbsp;</td>
230 - </tr>
231 - </table>
232 - </div>
233 - <div id=MeshSubMenuSpan style="display:none">
234 - <table id=MeshSubMenu cellpadding=0 cellspacing=0 class=style1>
235 - <tr>
236 - <td tabindex=0 id=MeshGeneral class="topbar_td style3x" onclick=go(20,event) onkeypress="if (event.key == 'Enter') go(20)">General</td>
237 - <td tabindex=0 id=MeshSummary class="topbar_td style3x" onclick=go(21,event) onkeypress="if (event.key == 'Enter') go(21)">Summary</td>
238 - <td class="topbar_td_end style3">&nbsp;</td>
239 - </tr>
240 - </table>
241 - </div>
242 - <div id=EventsSubMenuSpan style="display:none">
243 - <table id=EventsSubMenu cellpadding=0 cellspacing=0 class=style1>
244 - <tr>
245 - <td tabindex=0 id=EventsLive class="topbar_td style3x" onclick=go(3,event) onkeypress="if (event.key == 'Enter') go(3)">Events</td>
246 - <td tabindex=0 id=EventsReport class="topbar_td style3x" onclick=go(60,event) onkeypress="if (event.key == 'Enter') go(60)">Reports</td>
247 - <td class="topbar_td_end style3">&nbsp;</td>
248 - </tr>
249 - </table>
250 - </div>
251 - <div id=UserSubMenuSpan style="display:none">
252 - <table id=UserSubMenu cellpadding=0 cellspacing=0 class=style1>
253 - <tr>
254 - <td tabindex=0 id=UserGeneral class="topbar_td style3x" onclick=go(30,event) onkeypress="if (event.key == 'Enter') go(30)">General</td>
255 - <td tabindex=0 id=UserEvents class="topbar_td style3x" onclick=go(31,event) onkeypress="if (event.key == 'Enter') go(31)">Events</td>
256 - <td class="topbar_td_end style3">&nbsp;</td>
257 - </tr>
258 - </table>
259 - </div>
260 - <div id=UsersSubMenuSpan style="display:none">
261 - <table id=UsersSubMenu cellpadding=0 cellspacing=0 class=style1>
262 - <tr>
263 - <td tabindex=0 id=UsersGeneral class="topbar_td style3x" onclick=go(4,event) onkeypress="if (event.key == 'Enter') go(4)">Users</td>
264 - <td tabindex=0 id=UsersGroups class="topbar_td style3x" onclick=go(50,event) onkeypress="if (event.key == 'Enter') go(50)">Groups</td>
265 - <td tabindex=0 id=UsersRecordings class="topbar_td style3x" onclick=go(52,event) onkeypress="if (event.key == 'Enter') go(52)">Recordings</td>
266 - <td class="topbar_td_end style3">&nbsp;</td>
267 - </tr>
268 - </table>
269 - </div>
270 - <div id=ServerSubMenuSpan style="display:none">
271 - <table id=ServerSubMenu cellpadding=0 cellspacing=0 class=style1>
272 - <tr>
273 - <td tabindex=0 id=ServerGeneral class="topbar_td style3x" onclick=go(6,event) onkeypress="if (event.key == 'Enter') go(6)">General</td>
274 - <td tabindex=0 id=ServerStats class="topbar_td style3x" onclick=go(40,event) onkeypress="if (event.key == 'Enter') go(40)">Stats</td>
275 - <td tabindex=0 id=ServerConsole class="topbar_td style3x" onclick=go(115,event) onkeypress="if (event.key == 'Enter') go(115)">Console</td>
276 - <td tabindex=0 id=ServerTrace class="topbar_td style3x" onclick=go(41,event) onkeypress="if (event.key == 'Enter') go(41)">Trace</td>
277 - <td tabindex=0 id=ServerPlugins class="topbar_td style3x" onclick=go(42,event) onkeypress="if (event.key == 'Enter') go(42)">Plugins</td>
278 - <td class="topbar_td_end style3">&nbsp;</td>
279 - </tr>
280 - </table>
281 - </div>
282 - <div id=UserDummyMenuSpan>
283 - <table id=UserDummyMenu cellpadding=0 cellspacing=0 class=style1>
284 - <tr><td class=style3 style="">&nbsp;</td></tr>
285 - </table>
286 - </div>
287 - </div>
288 - </div>
289 - </div>
290 - <div id="column_l">
291 - <div id=p0 style="display:none">
292 - <div id=p0message><span id=p0span>Server disconnected</span>, <href onclick=reload() style=cursor:pointer><u>click to reconnect</u></href>.</div>
293 - </div>
294 - <div id=p1 style="display:none">
295 - <div id="p1title">
296 - <div style="display:none" id="devListToolbarViewIcons">
297 - <div id=devViewPageState style="float:left;line-height:32px;height:32px;font-size:16px;display:none"></div>
298 - <div tabindex=0 id=devViewPageButton1 class=viewSelector onclick=onDeviceViewPageChange(1) onkeypress="if (event.key == 'Enter') { onDeviceViewPageChange(1); }" title="Go to first page" style="display:none"><div class="viewSelector9"></div></div>
299 - <div tabindex=0 id=devViewPageButton2 class=viewSelector onclick=onDeviceViewPageChange(2) onkeypress="if (event.key == 'Enter') { onDeviceViewPageChange(2); }" title="Go to previous page" style="display:none"><div class="viewSelector10"></div></div>
300 - <div tabindex=0 id=devViewPageButton3 class=viewSelector onclick=onDeviceViewPageChange(3) onkeypress="if (event.key == 'Enter') { onDeviceViewPageChange(3); }" title="Go to next page" style="display:none"><div class="viewSelector11"></div></div>
301 - <div tabindex=0 id=devViewPageButton4 class=viewSelector onclick=onDeviceViewPageChange(4) onkeypress="if (event.key == 'Enter') { onDeviceViewPageChange(4); }" title="Go to last page" style="display:none;margin-right:12px"><div class="viewSelector8"></div></div>
302 - <div tabindex=0 id=devViewButton1 class=viewSelector onclick=onDeviceViewChange(1) onkeypress="if (event.key == 'Enter') { onDeviceViewChange(1); }" title="Columns"><div class="viewSelector2"></div></div>
303 - <div tabindex=0 id=devViewButton2 class=viewSelector onclick=onDeviceViewChange(2) onkeypress="if (event.key == 'Enter') { onDeviceViewChange(2); }" title="List"><div class="viewSelector1"></div></div>
304 - <div tabindex=0 id=devViewButton3 class=viewSelector onclick=onDeviceViewChange(3) onkeypress="if (event.key == 'Enter') { onDeviceViewChange(3); }" title="Desktops"><div class="viewSelector3"></div></div>
305 - <div tabindex=0 id=devViewButton5 class=viewSelector onclick=onDeviceViewChange(5) onkeypress="if (event.key == 'Enter') { onDeviceViewChange(5); }" title="Desktops"><div class="viewSelector7"></div></div>
306 - <div tabindex=0 id=devViewButton4 class=viewSelector onclick=onDeviceViewChange(4) onkeypress="if (event.key == 'Enter') { onDeviceViewChange(4); }" title="Map" style="display:none"><div class="viewSelector4"></div></div>
307 - </div><div><h1>My Devices</h1></div>
308 - </div>
309 - <table id="devListToolbarSpan" class="noselect">
310 - <tr>
311 - <td class=h1></td>
312 - <td id=devListToolbar class=style14 style="display:none">
313 - <img style="cursor:pointer;margin-top:4px;display:none" title="Collapse All" id="CollapseAllButton" src="images/icon-collapse.png" loading=lazy width=9 height=11 onclick="cmexpandaction(2)" />
314 - <img style="cursor:pointer;margin-top:4px;display:none" title="Expand All" id="ExpandAllButton" src="images/icon-expand.png" loading=lazy width=9 height=11 onclick="cmexpandaction(1)" />
315 - <input type=button id=SelectAllButton onclick="selectallButtonFunction();" value="Select All" />&nbsp;
316 - <input type=button id=GroupActionButton disabled="disabled" value="Group Action" onclick=groupActionFunction() />&nbsp;
317 - <input id=SearchInput type=search autocomplete=off placeholder=Filter onchange=onDeviceSearchChanged(event) onclick=onDeviceSearchChanged(event) onkeyup=onDeviceSearchChanged(event) onfocus=onSearchFocus(1) onblur=onSearchFocus(0) title="Filter: user:xxx or u:xxx ip:xxx group:xxx or g:xxx tag:xxx or t: xxx atag:xxx or a:xxx os:xxx amt:xxx desc:xxx wsc:ok wsc:noav wsc:noupdate wsc:nofirewall wsc:any"/>&nbsp;
318 - <span id=SearchInputClearButton style="display:none;position:relative"><img src="images/x16.png" type="button" onclick="clearDeviceSearch()" style="position:absolute;cursor:pointer;left:-18px;top:-8px" srcset="images/x32.png 2x"/></span>
319 - <select id=DevFilterSelect onchange=onOnlineCheckBox(event) title="Device Filter">
320 - <option value=0>All</option>
321 - <option value=1>Online</option>
322 - <option value=5>Offline</option>
323 - <option value=2>Sessions</option>
324 - <option value=3>Starred</option>
325 - <option value=4>Intel&reg; AMT</option>
326 - <option value=6>Help</option>
327 - <option value=7>Tagged</option>
328 - <option value=8>Untagged</option>
329 - </select>
330 - <label><input type=checkbox id=RealNameCheckBox onclick=onRealNameCheckBox() /><span title="Show devices operating system name">OS Name</span></label>
331 - <label style="display:none"><input type=checkbox id=OnlineCheckBox onclick=onOnlineCheckBox(event) /><span title="Only show devices that are online">Online</span></label>
332 - <span id="devsCustomUIBar"></span>
333 - </td>
334 - <td id=kvmListToolbar class=style14 style="display:none">
335 - <img style="cursor:pointer;margin-top:4px;display:none" title="Collapse All" id="CollapseAllButton2" src="images/icon-collapse.png" loading=lazy width=9 height=11 onclick="cmexpandaction(2)" />
336 - <img style="cursor:pointer;margin-top:4px;display:none" title="Expand All" id="ExpandAllButton2" src="images/icon-expand.png" loading=lazy width=9 height=11 onclick="cmexpandaction(1)" />
337 - <input type="button" onclick="connectAllKvmFunction()" value="Connect All" />&nbsp;
338 - <input type="button" onclick="disconnectAllKvmFunction()" value="Disconnect All" />&nbsp;
339 - <label><input type="checkbox" id="autoConnectDesktopCheckbox" onclick="autoConnectDesktops(event)" title="Automatic connect" />Auto&nbsp;</label>
340 - <input type="button" onclick="showMultiDesktopSettings()" value="Settings" />&nbsp;
341 - <input id=KvmSearchInput type=search placeholder=Filter onchange=onDeviceSearchChanged(event) onclick=onDeviceSearchChanged(event) onkeyup=onDeviceSearchChanged(event) autocomplete=off onfocus=onSearchFocus(1) onblur=onSearchFocus(0) />&nbsp;
342 - <span id=KvmSearchInputClearButton style="display:none;position:relative"><img src="images/x16.png" type="button" onclick="clearDeviceSearch()" style="position:absolute;cursor:pointer;left:-18px;top:-8px" srcset="images/x32.png 2x" /></span>
343 - </td>
344 - <td id=devMapToolbar class=style14 style="display:none">
345 - &nbsp;&nbsp;<input type=search id=mapSearchLocation placeholder="Search Location" onfocus=onMapSearchFocus(1) onblur=onMapSearchFocus(0) />
346 - <input type=button value=Search title="Search for location" onclick=getSearchLocation() />
347 - <input type=button id=refreshmap title="Reset map view" value=Reset onclick=refreshMap(false,true) />
348 - </td>
349 - <td class="auto-style1" style=height:100%>
350 - <img style="display:none;cursor:pointer;margin-top:3px" id=devListToolbarSettings src="images/icon-gear.png" loading=lazy width=16 height=16 onclick="onDeviceViewSettings()" />
351 - <div style="display:none" id=devListToolbarView>
352 - View
353 - <select id=viewselect onchange=onDeviceViewChange()>
354 - <option value=1>Columns</option>
355 - <option value=2>List</option>
356 - <option value=3>Desktops Fixed Width</option>
357 - <option id=viewselectmapoption value=4 style="display:none">Map</option>
358 - <option value=5>Desktops</option>
359 - </select>
360 - </div>
361 - <div style="display:none" id=devListToolbarSort>
362 - Sort
363 - <select id=sortselect onchange=mainUpdate(6)>
364 - <option>Group</option>
365 - <option>Power</option>
366 - <option>Device</option>
367 - <option>Tags</option>
368 - <option>Group-Tags</option>
369 - <option>Last Seen</option>
370 - </select>
371 - &nbsp;
372 - </div>
373 - <div style="display:none" id=devListToolbarSize>
374 - Size
375 - <select id=sizeselect onchange=onDeviceViewChange()>
376 - <option value=0>Small</option>
377 - <option value=1>Medium</option>
378 - <option value=2>Large</option>
379 - </select>
380 - &nbsp;
381 - </div>
382 - </td>
383 - <td class=h2></td>
384 - </tr>
385 - </table>
386 - <div id=NoMeshesPanel style="display:none">
387 - <table>
388 - <tr>
389 - <td valign="top" style="width:50px">
390 - <img src="images/info.png" loading="lazy" width="47" height="48" />
391 - </td>
392 - <td>
393 - <div id="getStarted1">To get started, <a href=# onclick="return account_createMesh()"><strong>click here to create a device group</strong></a>.</div>
394 - <div id="getStarted2">No device groups.</div>
395 - </td>
396 - </tr>
397 - </table>
398 - </div>
399 - <div id="xdevices" class="noselect" style="display:none" onscroll="onDevicesScroll()"></div>
400 - <div id="xdevicesmap" style="display:none">
401 - <div id=xmapSearchResultsDlg style="display:none">
402 - <div id=xmapSearchResultsBck>
403 - <div id=xmapSearchClose onclick=mapCloseSearchWindow()><b>X</b></div>
404 - <div style=padding:5px>Location Results</div>
405 - <div style=width:100%;margin:6px></div>
406 - </div>
407 - <div id=xmapSearchResults style=margin:6px></div>
408 - </div>
409 - </div>
410 - <div id="xmap-info-window"></div>
411 - </div>
412 - <div id=p2 style="display:none">
413 - <div id="p2title"><h1>My Account</h1></div>
414 - <div id="p2info" style="overflow-y:auto">
415 - <div id="p2AccountImageFrame">
416 - <img id="p2AccountImage" alt="" loading="lazy" width="128" height="128" onclick="account_manageImage(0)" src="images/user-256.png" />
417 - <div><a onclick="account_manageImage(0)">Change image</a></div>
418 - </div>
419 - <div id="p2AccountSecurity" style="display:none">
420 - <p><strong>Account security</strong></p>
421 - <div style="margin-left:25px">
422 - <div id="managePhoneNumber1"><div class="p2AccountActions"><span id="authPhoneNumberCheck"><strong>&#x2713;</strong></span></div><span><a href=# onclick="return account_managePhone()">Manage phone number</a><br /></span></div>
423 - <div id="manageEmail2FA"><div class="p2AccountActions"><span id="authEmailSetupCheck"><strong>&#x2713;</strong></span></div><span><a href=# onclick="return account_manageAuthEmail()">Manage email authentication</a><br /></span></div>
424 - <div id="manageAuthApp"><div class="p2AccountActions"><span id="authAppSetupCheck"><strong>&#x2713;</strong></span></div><span><a href=# onclick="return account_manageAuthApp()">Manage authenticator app</a><br /></span></div>
425 - <div id="manageHardwareOtp"><div class="p2AccountActions"><span id="authKeySetupCheck"><strong>&#x2713;</strong></span></div><span><a href=# onclick="return account_manageHardwareOtp(0)">Manage security keys</a><br /></span></div>
426 - <div id="managePushAuthDev"><div class="p2AccountActions"><span id="authPushAuthDevCheck"><strong>&#x2713;</strong></span></div><span><a href=# onclick="return account_managePushAuthDev()">Manage push authentication</a><br /></span></div>
427 - <div id="manageMessaging1"><div class="p2AccountActions"><span id="authMessagingCheck"><strong>&#x2713;</strong></span></div><span><a href=# onclick="return account_manageMessaging()">Manage messaging</a><br /></span></div>
428 - <div id="manageOtp"><div class="p2AccountActions"><span id="authCodesSetupCheck"><strong>&#x2713;</strong></span></div><span><a href=# onclick="return account_manageOtp(0)">Manage backup codes</a><br /></span></div>
429 - <div class="p2AccountActions"></div><span><a href=# onclick="return account_viewPreviousLogins()">View previous logins</a><br /></span>
430 - </div>
431 - </div>
432 - <div id="p2AccountActions">
433 - <p><strong>Account actions</strong></p>
434 - <p class="mL">
435 - <span id="managePhoneNumber2" style="display:none"><a href=# onclick="return account_managePhone()">Manage phone number</a><br /></span>
436 - <span id="manageMessaging2" style="display:none"><a href=# onclick="return account_manageMessaging()">Manage messaging</a><br /></span>
437 - <span id="verifyEmailId" style="display:none"><a href=# onclick="return account_showVerifyEmail()">Verify email</a><br /></span>
438 - <span id="accountEnableNotificationsSpan" style="display:none"><a href=# onclick="return account_enableNotifications()">Enable web notifications</a><br /></span>
439 - <a href=# onclick="return account_showLocalizationSettings()">Localization Settings</a><br />
440 - <a href=# onclick="return account_showAccountNotifySettings()">Notification Settings</a><br />
441 - <span id="p2AccountPassActions">
442 - <span id="accountChangeEmailAddressSpan" style="display:none"><a href=# onclick="return account_showChangeEmail()">Change email address</a><br /></span>
443 - <a href=# onclick="return account_showChangePassword()">Change password</a><span id="p2nextPasswordUpdateTime"></span><br />
444 - <a href=# onclick="return account_showDeleteAccount()">Delete account</a><br />
445 - </span>
446 - <span id="accountCreateLoginTokenSpan" style="display:none"><a href=# onclick="return account_createLoginToken()">Create login token</a><br /></span>
447 - </p>
448 - <br style=clear:both />
449 - </div>
450 - <div id=p2logintokens></div>
451 - <strong>Device Groups</strong>
452 - <span id="p2createMeshLink1"> - <a href=# onclick="return account_createMesh()" class="newMeshBtn"> New</a></span>
453 - <br /><br />
454 - <div id=p2meshes></div>
455 - <div id=p2noMeshFound style="display:none">No device groups.<span id="p2createMeshLink2"> <a href=# onclick="return account_createMesh()"><strong>Get started here!</strong></a></span></div>
456 - <br style=clear:both />
457 - </div>
458 - </div>
459 - <div id=p3 style="display:none">
460 - <div id="p3title"><h1>My Events</h1></div>
461 - <table class="pTable">
462 - <tr>
463 - <td class="h1"></td>
464 - <td class="auto-style1">
465 - <input type="button" style="display:none" value="Download Report" onclick=p3showReportDialog() />
466 - </td>
467 - <td class="auto-style1">
468 - Show
469 - <select id=p3limitdropdown onchange=refreshEvents()>
470 - <option value=60>Last 60</option>
471 - <option value=120>Last 120</option>
472 - <option value=250>Last 250</option>
473 - <option value=500>Last 500</option>
474 - <option value=1000>Last 1000</option>
475 - <option value="">No limit</option>
476 - </select>&nbsp;
477 - <a href=# onclick=p3showDownloadEventsDialog(2)><img src=images/link4.png height=10 width=10 title="Download Events" style=cursor:pointer></a>&nbsp;
478 - </td>
479 - <td class="h2"></td>
480 - </tr>
481 - </table>
482 - <div id=p3events></div>
483 - </div>
484 - <div id=p4 style="display:none">
485 - <div id="p4title"><h1>My Users</h1></div>
486 - <table class="pTable">
487 - <tr>
488 - <td class="h1"></td>
489 - <td class="style14">
490 - <div style="float:right">
491 - <input type=button onclick=showUserBroadcastDialog() style=margin-right:6px value="Broadcast" />
492 - <a href=# onclick=p4downloadUserInfo()><img style="cursor:pointer" title="Download user information" src="images/link4.png" /></a>
493 - <a href=# onclick=p4batchAccountCreate()><img id=p4UserBatchCreate style="cursor:pointer;display:none" title="Batch create many user accounts" src="images/link6.png" /></a>
494 - <img style="cursor:pointer;margin-top:3px;margin-left:6px" id=usersListToolbarSettings src="images/icon-gear.png" loading=lazy width=16 height=16 onclick="onUsersViewSettings()" />
495 - </div>
496 - <div>
497 - <input type=button id=UsersSelectAllButton onclick="p3usersSelectallButtonFunction()" value="Select All" />
498 - <input type=button id=UsersGroupActionButton disabled="disabled" value="Group Action" onclick=p3usersGroupActionFunction() />
499 - <input id=UserNewAccountButton type=button onclick=showCreateNewAccountDialog() value="New Account..." />
500 - <input id=UserSearchInput type=search style=width:120px;margin-left:6px placeholder=Filter onchange=onUserSearchInputChanged() onkeyup=onUserSearchInputChanged() autocomplete=off onfocus=onUserSearchFocus(1) onblur=onUserSearchFocus(0) />
501 - </div>
502 - </td>
503 - <td class="h2"></td>
504 - </tr>
505 - </table>
506 - <div id="p3users"></div>
507 - </div>
508 - <div id=p5 style="display:none">
509 - <div id="p5title"><h1>My Files</h1></div>
510 - <table id="p5toolbar" cellpadding="0" cellspacing="0">
511 - <tr>
512 - <td id="p5filehead" valign=bottom>
513 - <div id="p5rightOfButtons"></div>
514 - <div>
515 - <input type=button id=p5FolderUp disabled="disabled" onclick="return p5folderup();" value="Up" />&nbsp;
516 - <input type=button id=p5SelectAllButton disabled="disabled" onclick="p5selectallfile();" value="Select All" />&nbsp;
517 - <input type=button id=p5RenameFileButton disabled="disabled" value="Rename" onclick="p5renamefile();" />&nbsp;
518 - <input type=button id=p5DeleteFileButton disabled="disabled" value="Delete" onclick="p5deletefile();" />&nbsp;
519 - <input type=button id=p5ViewFileButton disabled="disabled" value="Edit" onclick="p5viewfile()" />&nbsp;
520 - <input type=button id=p5NewFolderButton disabled="disabled" value="New Folder" onclick="p5createfolder();" />&nbsp;
521 - <input type=button id=p5UploadButton disabled="disabled" value="Upload" onclick="p5uploadFile()" />&nbsp;
522 - <input type=button id=p5CutButton disabled="disabled" value="Cut" onclick="p5copyFile(1)" />&nbsp;
523 - <input type=button id=p5CopyButton disabled="disabled" value="Copy" onclick="p5copyFile(0)" />&nbsp;
524 - <input type=button id=p5PasteButton disabled="disabled" value="Paste" onclick="p5pasteFile()" />&nbsp;
525 - </div>
526 - </td>
527 - </tr>
528 - <tr>
529 - <td id="p5filesubhead">
530 - <div style=float:right>
531 - <select id=p5sortdropdown onchange=updateFiles()>
532 - <option value="1" selected="selected">Sort by name</option>
533 - <option value="2">Sort by size</option>
534 - <option value="3">Sort by date</option>
535 - <option value="4">Descend by name</option>
536 - <option value="5">Descend by size</option>
537 - <option value="6">Descend by date</option>
538 - </select>
539 - </div>
540 - <div>&nbsp;&nbsp;<span id="p5currentpath"></span></div>
541 - </td>
542 - </tr>
543 - </table>
544 - <div id="p5filetable">
545 - <!--
546 - <form id=p5fileCatchAll method=post enctype=multipart/form-data action=uploadfile.ashx target=fileUploadFrame>
547 - <input type=file id=p5fileCatchAllInput name=files style="position:absolute;left:0;width:100%;top:0;bottom:0;opacity:0;display:none" onchange="p5fileCatchAllInputChanged(event)" />
548 - <input id=p5fileDragLink2 name="link" style="display:none" />
549 - <input type=submit id=p5fileCatchAllSubmit style="display:none" />
550 - </form>
551 - -->
552 - <div id="p5PublicShare" style=""><div>These files are shared publicly, click "link" to get public url.</div></div>
553 - <div id="bigok" style="display:none"><b>&checkmark;</b></div>
554 - <div id="bigfail" style="display:none"><b>&#10007;</b></div>
555 - <span id="p5files"></span>
556 - </div>
557 - <table id="p5toolbarBottom" style=width:100% cellpadding=0 cellspacing=0>
558 - <tr><td class=style6>&nbsp;<span id="p5bottomstatus"></span></td></tr>
559 - </table>
560 - </div>
561 - <div id=p6 style="display:none">
562 - <div id="p6info" style="overflow-y:auto">
563 - <div id="p6title">
564 - <img id=MainMeshImage src="serverpic.ashx">
565 - <h1>My Server</h1>
566 - </div>
567 - <div id="p2ServerActions">
568 - <p><strong>Server actions</strong></p>
569 - <div style="margin-left:25px">
570 - <div id="p2ServerActionsBackup"><div class="p2AccountActions"><span style="display:none"><strong>&#x2713;</strong></span></div><a id="p6backuplink" href="{{{domainurl}}}backup.zip" rel="noreferrer noopener" target="_blank">Download server backup</a></div>
571 - <div id="p2ServerActionsRestore"><div class="p2AccountActions"><span style="display:none"><strong>&#x2713;</strong></span></div><a href=# onclick="return server_showRestoreDlg()">Restore server with backup</a></div>
572 - <div id="p2ServerActionsGoogleBackup" style="display:none"><div class="p2AccountActions"><span id="p2ServerActionsGoogleBackupCheck" style="display:none"><strong>&#x2713;</strong></span></div><span><a href=# onclick="return server_setupGoogleDriveBackup()">Google Drive backup</a><br /></span></div>
573 - <div id="p2ServerActionsVersion"><div class="p2AccountActions"><span style="display:none"><strong>&#x2713;</strong></span></div><a href=# onclick="return server_showVersionDlg()">Check server version</a></div>
574 - <div id="p2ServerActionsErrors"><div class="p2AccountActions"><span style="display:none"><strong>&#x2713;</strong></span></div><a href=# onclick="return server_showErrorsDlg()">Show server error log</a></div>
575 - </div>
576 - <br />
577 - </div>
578 - <strong>Server Statistics</strong><br /><br />
579 - <div id="serverStats">
580 - <div id="serverCpuChartView" style="display:none">
581 - <div class="chartViewCanvas"><canvas id="serverCpuChart"></canvas></div>
582 - <div class="chartViewText" id="serverCpuChartText"></div>
583 - </div>
584 - <div id="serverMemoryChartView" style="display:none">
585 - <div class="chartViewCanvas"><canvas id="serverMemoryChart"></canvas></div>
586 - <div class="chartViewText" id="serverMemoryChartText"></div>
587 - </div><br /><br />
588 - <div id="serverStatsTable"></div>
589 - </div>
590 - <div id="serverWarningsDiv" style="display:none">
591 - <br /><strong>Server Warnings</strong><br /><br />
592 - <div id="serverCertWarnings"></div>
593 - <div id="serverWarnings"></div>
594 - </div>
595 - </div>
596 - </div>
597 - <div id=p10 style="display:none">
598 - <div id=p10title>
599 - <div id="p10BackButton"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
600 - <h1>General - <span id=p10deviceName></span></h1>
601 - </div>
602 - <div id=p10info style="overflow-y:auto">
603 - <table style="width:100%" cellpadding="0" cellspacing="0">
604 - <tr>
605 - <td style=width:auto valign=top>
606 - <div id=p10html></div>
607 - </td>
608 - <td style=width:20px></td>
609 - <td style=width:200px;vertical-align:top;position:relative valign=top>
610 - <div class="deviceNotifyLargeDot">
611 - <div id="p10deviceMsg" onclick=showDeviceMessages(null,null,event) class=deviceNotifyDotSub></div>
612 - <img id="p10deviceStar" class=deviceNotifyDotSub src=images/icon-star-notify-16.png width=16 height=16>
613 - <img id="p10deviceNotify" onclick=showDeviceSessions(null,null,event) class=deviceNotifyDotSub src=images/icon-relay-notify.png width=16 height=16>
614 - <img id="p10deviceHelp" onclick=showDeviceHelpRequests(null,null,event) class=deviceNotifyDotSub src=images/icon-help-notify-16.png width=16 height=16>
615 - </div>
616 - <div id="p10deviceBattery" class="deviceBatteryLarge deviceBatteryLarge1"></div>
617 - <a href=# onclick=p10showiconselector()><img id=MainComputerImage></a>
618 - <div id=MainComputerState></div>
619 - </td>
620 - </tr>
621 - </table><br />
622 - <div id=p10html2></div>
623 - <div id=p10html3></div>
624 - <div id=p10html4></div>
625 - <div id=p10html5></div>
626 - </div>
627 - </div>
628 - <div id=p11 class="noselect" style="display:none">
629 - <div id="p11title">
630 - <div id=p11deviceNameHeader>
631 - <div id="p11BackButton"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
632 - <div id="devListToolbarViewIcons"><div class="viewSelector" onclick=deskToggleFull(event) title="Full Screen. Hold shift to browser full screen."><div class="viewSelector5"></div></div></div>
633 - <h1>Desktop - <span id=p11deviceName></span></h1>
634 - </div>
635 - </div>
636 - <div id="p11warning" onclick="showFeaturesDlg()">
637 - <div class="icon2"></div>
638 - <div class="warningbox">Redirection port or KVM feature is disabled<span id="p11warninga">, click here to enable it.</span></div>
639 - </div>
640 - <div id="p11warning2" onclick="showPowerActionDlg()">
641 - <div class="icon2"></div>
642 - <div class="warningbox">Remote computer is not powered on, click here to issue a power command.</div>
643 - </div>
644 - <div style="position:absolute;right:16px;margin-top:-14px;font-size:x-small;color:black">
645 - <div id="p11capslock" style="display:inline-block;margin-left:1px;border-radius:5px;background-color:#A3FFB8;padding:2px">
646 - CAPS
647 - </div>
648 - <div id="p11scrolllock" style="display:inline-block;margin-left:1px;border-radius:5px;background-color:#A3FFB8;padding:2px">
649 - SCROLL
650 - </div>
651 - <div id="p11numlock" style="display:inline-block;margin-left:1px;border-radius:5px;background-color:#A3FFB8;padding:2px">
652 - NUM
653 - </div>
654 - </div>
655 - <div id=deskarea0 cellpadding=0 cellspacing=0>
656 - <div id=deskarea1 class="areaHead">
657 - <div class="toright2">
658 - <div id="idx_deskFullBtn2" onclick=deskToggleFull(event) style="float:right">&nbsp;&#x2716;</div>
659 - <span id="p11power"></span>&nbsp;
660 - <div class='deskareaicon' title="Toggle View Mode" onclick="toggleAspectRatio(1)">&#8690;</div>
661 - <div class='deskareaicon' title="Rotate Left" onclick="drotate(-1)">&olarr;</div>
662 - <div class='deskareaicon' title="Rotate Right" onclick="drotate(1)">&orarr;</div>
663 - <div id="deskRecordIcon" class='deskareaicon' title="Server is recording this session" style="display:none;background-color:red;width:12px;height:12px;border-radius:6px;margin-top:5px"></div>
664 - <input id="deskFocusBtn" type="button" title="Toggle focus mode, when active only the region around the mouse is updated" onkeypress="return false" onkeydown="return false" value="Focus All" onclick="deskToggleFocus()" style="margin-right:3px;display:none" />
665 - <input id="deskActionsBtn" type=button title="Perform power actions on the device" onkeypress="return false" onkeydown="return false" value=Actions onclick=deviceActionFunction() class="mR" />
666 - <input id="deskActionsSettings" type="button" value="Settings..." title="Edit remote desktop settings" onkeypress="return false" onkeydown="return false" onclick="showDesktopSettings()" class="mR" />
667 - <input type="button" title="Change the power state of the remote machine" onkeypress="return false" onkeydown="return false" value="Power Actions..." onclick="showPowerActionDlg()" style="display:none" />
668 - <div id="desktopCustomUiButtons" style="float:left"></div>
669 - </div>
670 - <div>
671 - <input type="button" id="autoconnectbutton1" value="AutoConnect" onclick=autoConnectDesktop(event) onkeypress="return false" onkeydown="return false" style="display:none;margin-right:4px" />
672 - <span id=connectbutton1span><input type=button id=connectbutton1 cmenu="deskConnectButton" title="Connect using MeshAgent remote desktop" value="Connect" onclick=connectDesktop(event,3) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span>
673 - <span id=connectbutton1rspan><input type=button id=connectbutton1r cmenu="altPortContextMenu" value="RDP Connect" title="Connect using RDP" onclick=askRdpCredentials() onkeypress="return false" onkeydown="return false" disabled="disabled" /></span>
674 - <span id=connectbutton1hspan><input type=button id=connectbutton1h value="HW Connect" title="Connect using hardware KVM" onclick=connectDesktop(event,2) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span>
675 - <span id=disconnectbutton1span><input type=button id=disconnectbutton1 cmenu="deskDisconnectButton" value="Disconnect" onclick=connectDesktop(event,0) onkeypress="return false" onkeydown="return false" /></span>
676 - <span id="deskstatus" style="line-height:22px">Disconnected</span><span id="deskmetadata"></span>
677 - </div>
678 - </div>
679 - <div id=deskarea3x>
680 - <div id="p11bigok" style="display:none"><b>&checkmark;</b></div>
681 - <div id="p11bigfail" style="display:none"><b>&#10007;</b></div>
682 - <div id=DeskFocus oncontextmenu="return false" onmousedown=dmousedown(event) onmouseup=dmouseup(event) onmousemove=dmousemove(event)></div>
683 - <div id=DeskParent>
684 - <canvas id=Desk width=640 height=480 oncontextmenu="return false" onmousedown=dmousedown(event) onmouseup=dmouseup(event) onmousemove=dmousemove(event) onmousewheel=dmousewheel(event)></canvas>
685 - </div>
686 - <div id=DeskTools>
687 - <div id=deskToolsAreaTop>
688 - <a id=DeskToolsRefreshButton style="right:2px" onclick="refreshDeskTools()">Refresh</a>
689 - <div id=deskToolsTopTabProcess class="deskToolsTopTab" onclick="changeDeskToolTab(0)" style="left:0px;bottom:0px">Processes</div>
690 - <div id=deskToolsTopTabService class="deskToolsTopTab" onclick="changeDeskToolTab(1)" style="display:none;left:90px;color:gray">Services</div>
691 - </div>
692 - <div id=deskToolsArea>
693 - <div id="DeskToolsProcessTab">
694 - <div id=deskToolsHeader>
695 - <a class="colmn1" title="Sort by process id" onclick=sortProcess(0)>PID</a>
696 - <a class="colmn2" title="Sort by name" onclick=sortProcess(1)>Name</a>
697 - </div>
698 - <div id="DeskToolsProcesses" style=""></div>
699 - </div>
700 - <div id="DeskToolsServiceTab" style="display:none">
701 - <div id=deskToolsServiceHeader>
702 - <a class="colmn1" style="width:70px" title="Sort by state" onclick=sortService(0)>State</a>
703 - <a class="colmn2" title="Sort by name" onclick=sortService(1)>Name</a>
704 - </div>
705 - <div id="DeskToolsServices" style=""></div>
706 - </div>
707 - </div>
708 - </div>
709 - <div id=p11DeskConsoleMsg style="display:none;text-align:left;cursor:pointer;position:absolute;left:30px;top:17px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px;text-align:left" onclick=p11clearConsoleMsg()></div>
710 - <div id=p11DeskSessionSelector style="display:none;position:absolute;left:30px;top:17px;right:30px;bottom:17px;overflow-y:auto"></div>
711 - </div>
712 - <div id=deskarea4 class="areaFoot">
713 - <div class="toright2">
714 - <span id="DeskLatency" title="Desktop Session Latency"></span>
715 - <span id="DeskTimer" style="line-height:22px" title="Session time"></span>
716 - <input id=DeskToolsButton type=button value=Tools title="Toggle tools view" onkeypress="return false" onkeydown="return false" onclick="toggleDeskTools()" />
717 - <span>&nbsp;</span>
718 - <span id=DeskRunButton cmenu="deskPreConfigScriptContextMenu" class="deskarea" title="Run a script on this computer"><img class="desktopButtons" src='images/icon-play.png' onclick=runDeviceCmd() height=16 width=16 style=padding-top:2px /></span>
719 - <span id=DeskChatButton class="deskarea" title="Open chat window to this computer"><img class="desktopButtons" src='images/icon-chat.png' onclick=deviceChat(event) height=16 width=16 style=padding-top:2px /></span>
720 - <span id=DeskNotifyButton title="Display a notification on the remote computer"><img class="desktopButtons" src='images/icon-notify.png' onclick=deviceToastFunction() height=16 width=16 style=padding-top:2px /></span>
721 - <span id=DeskLockButton title="Lock the remote computer"><img src='images/icon-lock.png' class="desktopButtons" onclick=deviceLockFunction() height=16 width=16 /></span>
722 - <span id=DeskOpenWebButton title="Open a web address on the remote computer"><img class="desktopButtons" src='images/icon-url2.png' onclick=deviceUrlFunction() height=16 width=16 /></span>
723 - <span id=DeskBackgroundButton title="Toggle remote desktop background"><img class="desktopButtons" src='images/icon-background.png' onclick=deviceToggleBackground(event) height=16 width=16 /></span>
724 - <span id=DeskSaveImageButton title="Save a screenshot of the remote desktop"><img class="desktopButtons" src='images/icon-camera.png' onclick=deskSaveImage() height=16 width=16 /></span>
725 - <span id=DeskRecordButton cmenu=deskPlayerContextMenu title="Record remote desktop session to file" style="display:none"><img class="desktopButtons" id=DeskRecordButtonImage src='images/icon-film.png' onclick=deskRecordSession() height=16 width=16 /></span>
726 - <span id=DeskClipboardInButton title="Download remote clipboard to local clipboard" style="display:none"><img class="desktopButtons" id=DeskClipboardInButtonImage src='images/icon-clipboard-in.png' onclick=deskClipboardInFunction() height=16 width=16 /></span>
727 - <span id=DeskClipboardOutButton title="Upload local clipboard to remote device"><img class="desktopButtons" id=DeskClipboardOutButtonImage src='images/icon-clipboard-out.png' onclick=deskClipboardOutFunction() height=16 width=16 /></span>
728 - <span id=DeskRefreshButton title="Refresh the desktop"><img class="desktopButtons" id=DeskRefreshButtonImage src='images/icon-refresh.png' onclick=deskRefreshFunction() height=16 width=16 /></span>
729 - <span id=DeskInputLockedButton title="Remote input is locked"><img class="desktopButtons" id=DeskInputLockedButtonImage src='images/icon-keylock-red.png' onclick=deskInputLockFunction(0) height=16 width=16 /></span>
730 - <span id=DeskInputUnLockedButton title="Remote input is unlocked"><img class="desktopButtons" id=DeskInputUnLockedButtonImage src='images/icon-keylock.png' onclick=deskInputLockFunction(1) height=16 width=16 /></span>
731 - <span id=DeskGuestShareButton title="Share the device with a guest"><img class="desktopButtons" id=DeskInputUnLockedButtonImage src='images/icon-share2.png' onclick=showShareDevice() height=16 width=16 /></span>
732 - </div>
733 - <div class="toright2"><span id=DeskMonitorSelectionSpan></span></div>
734 - <div>
735 - <select id="deskkeys" cmenu=deskKeyShortcutContextMenu></select>
736 - <input id="DeskWD" type=button value="Send" onkeypress="return false" onkeydown="return false" onclick="deskSendKeys()" />
737 - <input id="DeskESC" style="display:none" type="button" value="ESC" onkeypress="return false" onkeydown="return false" onclick="sendDeskEsc()" />
738 - <input id="DeskClip" type="button" value="Clipboard" onkeypress="return false" onkeydown="return false" onclick="showDeskClip()" />
739 - <input id="DeskType" cmenu="deskPreConfigShortcutContextMenu" type="button" value="Type" onkeypress="return false" onkeydown="return false" onclick="showDeskType()" />
740 - <label><span id="DeskControlSpan" title="Toggle mouse and keyboard input"><input id="DeskControl" type="checkbox" onkeypress="return false" onkeydown="return false" onclick="toggleKvmControl()" />Input</span></label>&nbsp;
741 - </div>
742 - </div>
743 - </div>
744 - </div>
745 - <div id=p12 style="display:none">
746 - <div id="p12title">
747 - <div id="p12BackButton"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
748 - <div id="devListToolbarViewIcons2" style="float:right"><div class="viewSelector" onclick=deskToggleFull(event) title="Full Screen. Hold shift to browser full screen."><div class="viewSelector5"></div></div></div>
749 - <h1>Terminal - <span id=p12deviceName></span></h1>
750 - </div>
751 - <div id="p12warning" onclick=showFeaturesDlg()>
752 - <div class="icon2"></div>
753 - <div class="warningbox">Redirection port or KVM feature is disabled<span id="p12warninga">, click here to enable it.</span></div>
754 - </div>
755 - <div id="p12warning2" onclick=showPowerActionDlg()>
756 - <div class="icon2"></div>
757 - <div class="warningbox">Remote computer is not powered on, click here to issue a power command.</div>
758 - </div>
759 - <div id=termTable style="position:relative">
760 - <table style="width:100%" cellpadding=0 cellspacing=0>
761 - <tr>
762 - <td class="areaHead" style="line-height:22px">
763 - <div class="toright2">
764 - <div id="idx_termFullBtn2" onclick=deskToggleFull(event) style="float:right">&nbsp;&#x2716;</div>
765 - <div id="termRecordIcon" class='deskareaicon' title="Server is recording this session" style="display:none;background-color:red;width:12px;height:12px;border-radius:6px;margin-top:5px;margin-left:5px"></div>
766 - <input id="termActionsBtn" type=button title="Perform power actions on the device" onkeypress="return false" onkeydown="return false" value=Actions onclick=deviceActionFunction() />
767 - <div id="terminalCustomUpperRight" style="float:left;margin-right:6px"></div>
768 - <div id="terminalCustomUiButtons" style="float:left"></div>
769 - </div>
770 - <div>
771 - <input type="button" id="autoconnectbutton2" value="AutoConnect" onclick=autoConnectTerminal(event) onkeypress="return false" onkeydown="return false" style="display:none" /><span id="connectbutton2span" style="margin-right:4px"><input type="button" id="connectbutton2" cmenu="termConnectButton" value="Connect" onclick=connectTerminal(event,1) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span><span id="connectbutton2sspan" style="margin-right:4px"><input type="button" id="connectbutton2s" cmenu=sshPortContextMenu value="SSH Connect" onclick=connectTerminal(event,3) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span><span id="connectbutton2hspan" style="margin-right:4px"><input type="button" id="connectbutton2h" value="HW Connect" title="Connect using Intel&reg; AMT hardware KVM" onclick=connectTerminal(event,2) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span><span id="disconnectbutton2span" style="margin-right:4px"><input type="button" id="disconnectbutton2" value="Disconnect" onclick=connectTerminal(event,0) onkeypress="return false" onkeydown="return false" /></span>
772 - <span id="termstatus" style="line-height:22px">Disconnected</span><span id="termtitle"></span>
773 - </div>
774 - </td>
775 - </tr>
776 - <tr>
777 - <td id="termarea3x">
778 - <div id="termarea3xdiv" style="width:100%;height:100%;text-align:left"></div>
779 - <pre id="Term"></pre>
780 - </td>
781 - </tr>
782 - <tr>
783 - <td class="areaFoot">
784 - <div class="toright2">
785 - <span id="TermTimer" title="Session time"></span>&nbsp;
786 - <span id="terminalSettingsButtons" style="display:none">
787 - <input id="id_tcrbutton" type="button" onkeypress="return false" onkeydown="return false" class="bottombutton" value="CR+LF" title="Toggle what the return key will send" onclick="termToggleCr()" />
788 - <input id="id_tfxkeysbutton" type="button" onkeypress="return false" onkeydown="return false" class="bottombutton" value="Intel (F10 = ESC+[OM)" title="Toggle F1 to F10 keys emulation type" onclick="termToggleFx()" />
789 - <input id="id_ttypebutton" type="button" onkeypress="return false" onkeydown="return false" class="bottombutton" value="Extended Ascii" title="Toggle terminal emulation type" onclick="termToggleType()" />
790 - </span>
791 - <span id="terminalSizeDropDown" style="display:none">
792 - <select id="termSizeList" onkeypress="return false"><option value="1">80x25</option><option value="2">100x30</option></select>
793 - </span>
794 - <span id="specialKeyDropDown">
795 - <select id="specialkeylist" onkeypress="return false"></select>
796 - <input id="specialkeylistinput" type="button" onkeypress="return false" class="bottombutton" value="Send" title="Send the selected special key" onclick="sendSpecialKey()" />
797 - </span>
798 - </div>
799 - <div>
800 - &nbsp;
801 - <input type=button onkeypress="return false" onkeydown="return false" class="bottombutton" id="ctrlcbutton" value="Ctl-C" onclick="termSendKey(3,'ctrlcbutton')" />
802 - <input type=button onkeypress="return false" onkeydown="return false" class="bottombutton" id="ctrlxbutton" value="Ctl-X" onclick="termSendKey(24,'ctrlxbutton')" />
803 - <input type=button onkeypress="return false" onkeydown="return false" class="bottombutton" id="escbutton" value="ESC" onclick="termSendKey(27,'escbutton')" />
804 - <input type=button onkeypress="return false" onkeydown="return false" class="bottombutton" id="bsbutton" value="Backspace" onclick="termSendKey(8,'bsbutton')" style="display:none" />
805 - <input type=button onkeypress="return false" onkeydown="return false" class="bottombutton" id="pastebutton" value="Paste" title="Paste text into the terminal" onclick="showTermPasteDialog()" style="display:none" />
806 - </div>
807 - </td>
808 - </tr>
809 - </table>
810 - <div id=p12TermConsoleMsg style="display:none;text-align:left;cursor:pointer;position:absolute;left:30px;top:45px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=p12clearConsoleMsg()></div>
811 - </div>
812 - </div>
813 - <div id=p13 style="display:none">
814 - <div id="p13title">
815 - <div id="p13BackButton" style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
816 - <h1>Files - <span id=p13deviceName></span></h1>
817 - </div>
818 - <table id="p13toolbar" cellpadding="0" cellspacing="0">
819 - <tr>
820 - <td class="areaHead" style="line-height:22px">
821 - <div class="toright2">
822 - <input id="filesActionsBtn" type=button title="Perform power actions on the device" value=Actions onclick=deviceActionFunction() />
823 - <div id="filesRecordIcon" class='deskareaicon' title="Server is recording this session" style="display:none;background-color:red;width:12px;height:12px;border-radius:6px;margin-top:5px;margin-left:5px"></div>
824 - <div id="filesCustomUpperRight" style="float:left;margin-right:6px"></div>
825 - <div id="filesCustomUiButtons" style="float:left"></div>
826 - </div>
827 - <div>
828 - <input id=p13AutoConnect value="AutoConnect" onclick=autoConnectFiles(event) type="button" style="display:none" />
829 - <input id=p13Connect value="Connect" cmenu="filesConnectButton" onclick=connectFiles(event,1) type="button" />
830 - <input id=p13Connects value="SFTP Connect" cmenu=sshPortContextMenu onclick=connectFiles(event,2) type="button" />
831 - <input id=p13Disconnect value="Disconnect" onclick=connectFiles(event) type="button" />
832 - <span id=p13Status>Disconnected</span>
833 - </div>
834 - </td>
835 - </tr>
836 - <tr>
837 - <td class="areaHead2" valign=bottom>
838 - <div id="p13rightOfButtons" class="toright2"></div>
839 - <div>
840 - <input type=button style="margin-right:2px" disabled="disabled" id=p13FolderUp value="Up" onclick="p13folderup()" />
841 - <input type=button style="margin-right:2px" disabled="disabled" id=p13SelectAllButton value="Select All" onclick="p13selectallfile()" />
842 - <input type=button style="margin-right:2px" disabled="disabled" id=p13RenameFileButton value="Rename" onclick="p13renamefile()" />
843 - <input type=button style="margin-right:2px" disabled="disabled" id=p13DeleteFileButton value="Delete" onclick="p13deletefile()" />
844 - <input type=button style="margin-right:2px" disabled="disabled" id=p13ViewFileButton value="Edit" onclick="p13viewfile()" />
845 - <input type=button style="margin-right:2px" disabled="disabled" id=p13NewFolderButton value="New Folder" onclick="p13createfolder()" />
846 - <input type=button style="margin-right:2px" disabled="disabled" id=p13UploadButton value="Upload" onclick="p13uploadFile()" />
847 - <input type=button style="margin-right:2px" disabled="disabled" id=p13CutButton value="Cut" onclick="p13copyFile(1)" />
848 - <input type=button style="margin-right:2px" disabled="disabled" id=p13CopyButton value="Copy" onclick="p13copyFile(0)" />
849 - <input type=button style="margin-right:2px" disabled="disabled" id=p13PasteButton value="Paste" onclick="p13pasteFile()" />
850 - <input type=button style="margin-right:2px" disabled="disabled" id=p13ZipButton value="Zip" onclick="p13zipFiles()" />
851 - <input type=button style="margin-right:2px" disabled="disabled" id=p13RefreshButton value="Refresh" onclick="p13folderup(9999)" />
852 - <input type=button style="margin-right:2px" disabled="disabled" id=p13FindButton value="Find" onclick="p13findfile()" />
853 - </div>
854 - </td>
855 - </tr>
856 - <tr>
857 - <td class="areaHead3">
858 - <div class="toright2">
859 - <select id=p13sortdropdown onchange=p13updateFiles()>
860 - <option value=1 selected="selected">Sort by name</option>
861 - <option value=2>Sort by size</option>
862 - <option value=3>Sort by date</option>
863 - <option value=4>Descend by name</option>
864 - <option value=5>Descend by size</option>
865 - <option value=6>Descend by date</option>
866 - </select>
867 - </div>
868 - <div>&nbsp;&nbsp;<span id="p13currentpath"></span></div>
869 - </td>
870 - </tr>
871 - </table>
872 - <div id=p13FilesConsoleMsg style="display:none;text-align:left;cursor:pointer;position:absolute;left:30px;top:165px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick=p13clearConsoleMsg()></div>
873 - <div id="p13filetable" style="">
874 - <div id="p13bigok" style="display:none"><b>&checkmark;</b></div>
875 - <div id="p13bigfail" style="display:none"><b>&#10007;</b></div>
876 - <span id="p13files"></span>
877 - </div>
878 - <table id="p13toolbarBottom" cellpadding=0 cellspacing=0>
879 - <tr><td class=style6>&nbsp;<span id="p13bottomstatus"></span></td></tr>
880 - </table>
881 - </div>
882 - <div id=p14 style="display:none">
883 - <div id="p14title">
884 - <div id="p14BackButton" style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
885 - <div id="devListToolbarViewIcons"><div class="viewSelector" onclick=deskToggleFull(event) title="Full Screen. Hold shift to browser full screen."><div class="viewSelector5"></div></div></div>
886 - <h1><span id=p14deviceNamePrefix>Intel&reg; AMT</span> - <span id=p14deviceName></span></h1>
887 - </div>
888 - <iframe id=p14iframe src="{{{domainurl}}}commander.ashx"></iframe>
889 - </div>
890 - <div id=p15 style="display:none">
891 - <div id="p15title">
892 - <div id="p15BackButton" style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
893 - <h1><span id=p15deviceName></span></h1>
894 - </div>
895 - <table id="consoleTable" cellpadding=0 cellspacing=0>
896 - <tr>
897 - <td class="areaHead">
898 - <div class="toright2">
899 - <div id=p15coreName title="Information about current core running on this agent"></div>
900 - <input type=button id=p15uploadCore value="Agent Action" onclick=p15uploadCore(event) title="Change the agent Java Script code module" />
901 - <img onclick=p15downloadConsoleText() style="cursor:pointer;margin-top:6px" title="Download console text" src="images/link4.png" />
902 - </div>
903 - <div id="p15statetext"></div>
904 - </td>
905 - </tr>
906 - <tr>
907 - <td>
908 - <div class="areaProgress"><div id="consoleprogressbar" style=""></div></div>
909 - </td>
910 - </tr>
911 - <tr>
912 - <td id=p15agentConsole>
913 - <pre id=p15agentConsoleText></pre>
914 - </td>
915 - </tr>
916 - <tr>
917 - <td class="areaFoot">
918 - <table style="width:100%">
919 - <tr>
920 - <td style="width:99%">
921 - <input id=p15consoleText style=width:100% onkeyup=p15consoleSend(event) onfocus=onConsoleFocus(1) onblur=onConsoleFocus(0) />
922 - </td>
923 - <td>&nbsp;</td>
924 - <td id="p15outputselecttd">
925 - <select id=p15outputselect onchange="setupConsole()">
926 - <option id="p15outputselect1" value=1>Agent</option>
927 - <option id="p15outputselect3" value=3>Push</option>
928 - <option id="p15outputselect2" value=2>MQTT</option>
929 - </select>
930 - </td>
931 - <td style="width:1%"><input id="id_p15consoleClear" type="button" class="bottombutton" value="Clear" onclick="p15consoleClear()" /></td>
932 - </tr>
933 - </table>
934 - </td>
935 - </tr>
936 - </table>
937 - </div>
938 - <div id=p16 style="display:none">
939 - <div id="p16title">
940 - <div id="p16BackButton" style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
941 - <h1>Events - <span id=p16deviceName></span></h1>
942 - </div>
943 - <table class="pTable">
944 - <tr>
945 - <td class="h1"></td>
946 - <!--<td>&nbsp;<input type=button onclick=refreshDeviceEvents() value="Refresh" /></td>-->
947 - <td class="auto-style1">
948 - Show
949 - <select id=p16limitdropdown onchange=refreshDeviceEvents()>
950 - <option value=60>Last 60</option>
951 - <option value=120>Last 120</option>
952 - <option value=250>Last 250</option>
953 - <option value=500>Last 500</option>
954 - <option value=1000>Last 1000</option>
955 - </select>
956 - <a href=# onclick=p3showDownloadEventsDialog(1)><img src=images/link4.png height=10 width=10 title="Download Events" style=cursor:pointer></a>&nbsp;
957 - </td>
958 - <td class="h2"></td>
959 - </tr>
960 - </table>
961 - <div id=p16events></div>
962 - </div>
963 - <div id=p17 style="display:none;margin-left:-18px">
964 - <div id="p17title" style="margin-left:18px">
965 - <div id="p17BackButton" style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
966 - <div id="devListToolbarViewIcons3"><div class="viewSelector" onclick=deskToggleCpuGraph(event) title="Show device CPU and memory usage."><div class="viewSelector6"></div></div></div>
967 - <h1>Details - <span id=p17deviceName></span></h1>
968 - </div>
969 - <div id=p17info style="overflow-y:auto">
970 - <div id=p17graph style="width:100%">
971 - <table style=width:100%>
972 - <tr>
973 - <td style=width:64px;vertical-align:top>
974 - <img src=images/details/graph64.png border=0 width=64 />
975 - </td>
976 - <td>
977 - <div class=DevSt style=margin-bottom:3px;margin-left:16px><b>Live Graph</b></div>
978 - <div style="margin-bottom:10px;margin-left:16px;height:240px;width:calc(100% - 16px);position:relative">
979 - <canvas id=deviceDetailsStats style="position:absolute;top:0;left:0;right:0;bottom:0"></canvas>
980 - </div>
981 - <div id="extraGraphValues" style="display:none;margin-left:30px;margin-bottom:15px"></div>
982 - </td>
983 - </tr>
984 - </table>
985 - </div>
986 - <div id=p17info2></div>
987 - </div>
988 - </div>
989 - <div id=p19 style="display:none">
990 - <div id="p19title">
991 - <div id="p19BackButton" style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
992 - <h1>Plugins - <span id=p19deviceName></span></h1>
993 - </div>
994 - <div id="p19headers"></div>
995 - <div id=p19pages></div>
996 - </div>
997 - <div id=p20 style="display:none">
998 - <div id=p20main style="overflow-y:auto">
999 - <div id="p20title">
1000 - <picture id=MainMeshImage style=border-width:0px;height:200px;width:200px;float:right>
1001 - <source type="image/webp" width=200 height=200 srcset="images/webp/mesh-256.webp" />
1002 - <img alt="" width=200 height=200 src=images/mesh-256.png />
1003 - </picture>
1004 - <div style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
1005 - <h1>General - <span id=p20meshName></span></h1>
1006 - </div>
1007 - <p id=p20info></p>
1008 - <p id=p20info2></p>
1009 - </div>
1010 - </div>
1011 - <div id=p21 style="display:none">
1012 - <div id="p21title">
1013 - <div style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
1014 - <h1>Summary - <span id=p21meshName></span></h1>
1015 - </div>
1016 - <div id=p21main style="overflow-y:auto">
1017 - <div style="width:100%">
1018 - <div style="display:table;width:93%">
1019 - <div id="meshPowerChartDiv" style="width:23%;display:inline-block;text-align:center;max-width:250px">
1020 - <div style="margin:10px;font-size:16px">Power States</div>
1021 - <canvas id="meshPowerChart" style="width:250px;height:250px"></canvas>
1022 - </div>
1023 - <div id="meshOsChartDiv" style="width:23%;display:inline-block;text-align:center;max-width:250px">
1024 - <div style="margin:10px;font-size:16px">Agent Types</div>
1025 - <canvas id="meshOsChart" style="width:250px;height:250px"></canvas>
1026 - </div>
1027 - <div id="meshConnChartDiv" style="width:23%;display:inline-block;text-align:center;max-width:250px">
1028 - <div style="margin:10px;font-size:16px">Connectivity</div>
1029 - <canvas id="meshConnChart" style="width:250px;height:250px"></canvas>
1030 - </div>
1031 - <div id="meshSecurityChartDiv" style="width:23%;display:inline-block;text-align:center;max-width:250px">
1032 - <div style="margin:10px;font-size:16px">Security</div>
1033 - <canvas id="meshSecurityChart" style="width:250px;height:250px"></canvas>
1034 - </div>
1035 - </div>
1036 - </div>
1037 - <p id=p21info style="overflow-y:auto"></p>
1038 - </div>
1039 - </div>
1040 - <div id=p30 style="display:none">
1041 - <div id="p30title">
1042 - <div style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
1043 - <h1>General - <span id=p30userName></span></h1>
1044 - </div>
1045 - <div id="p30info" style="overflow-y:auto">
1046 - <table style="width:100%" cellpadding="0" cellspacing="0">
1047 - <tr>
1048 - <td style=width:auto valign=top>
1049 - <div id=p30html></div>
1050 - </td>
1051 - <td style=width:20px></td>
1052 - <td style=width:200px;position:relative valign=top>
1053 - <img id="p30userAuthServiceLogo" loading="lazy" style="display:none" class=userAuthStrategyLogo width=64 height=64>
1054 - <picture id=MainUserImage style="display:none;border-width:0px;height:200px;width:200px;float:right" onclick="account_manageImage(1)">
1055 - <source type="image/webp" width=200 height=200 srcset="images/webp/user-256.webp" />
1056 - <img alt="" width=200 height=200 src=images/user-256.png />
1057 - </picture>
1058 - <img id=MainUserImageEx alt="" width=200 height=200 src=images/user-256.png onclick="account_manageImage(1)" />
1059 - <div style="width:100%;text-align:center"><strong><span id=MainUserState></span></strong></div>
1060 - </td>
1061 - </tr>
1062 - </table><br />
1063 - <div id=p30html2></div>
1064 - <div id=p30html3></div>
1065 - </div>
1066 - </div>
1067 - <div id=p31 style="display:none">
1068 - <div id="p31title">
1069 - <div style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
1070 - <h1>Events - <span id=p31userName></span></h1>
1071 - </div>
1072 - <table class="pTable">
1073 - <tr>
1074 - <td class="h1"></td>
1075 - <!--<td>&nbsp;<input type=button onclick=refreshUsersEvents() value="Refresh" /></td>-->
1076 - <td class="auto-style1">
1077 - Show
1078 - <select id=p31limitdropdown onchange=refreshUsersEvents()>
1079 - <option value=60>Last 60</option>
1080 - <option value=120>Last 120</option>
1081 - <option value=250>Last 250</option>
1082 - <option value=500>Last 500</option>
1083 - <option value=1000>Last 1000</option>
1084 - <option value="">No limit</option>
1085 - </select>
1086 - <a href=# onclick=p3showDownloadEventsDialog(3)><img src=images/link4.png height=10 width=10 title="Download Events" style=cursor:pointer></a>&nbsp;
1087 - </td>
1088 - <td class="h2"></td>
1089 - </tr>
1090 - </table>
1091 - <div id=p31events style=""></div>
1092 - </div>
1093 - <div id=p40 style="display:none">
1094 - <div id="p40title"><h1>My Server Stats</h1></div>
1095 - <div class="areaHead">
1096 - <div class="toright2">
1097 - <select id=p40server style="display:none" onchange=updateServerTimelineStats()>
1098 - </select>&nbsp;
1099 - <select id=p40type onchange=updateServerTimelineStats()>
1100 - <option value=0>Connections</option>
1101 - <option value=1>Memory</option>
1102 - <option value=5>CPU</option>
1103 - <option value=3>Inbound Traffic</option>
1104 - <option value=4>Outbound Traffic</option>
1105 - </select>&nbsp;
1106 - <select id=p40time onchange=updateServerTimelineHours()>
1107 - <option value=3>Last 3 hours</option>
1108 - <option value=8>Last 8 hours</option>
1109 - <option value=24>Last day</option>
1110 - <option value=168>Last week</option>
1111 - <option value=720>Last 30 days</option>
1112 - </select>&nbsp;
1113 - <img src=images/link4.png height=10 width=10 title="Download data points (.csv)" style=cursor:pointer onclick=p40downloadEvents()>&nbsp;
1114 - </div>
1115 - <div>
1116 - <input value="Refresh" type="button" onclick="refreshServerTimelineStats()" />
1117 - &nbsp;<label><input id=p40log type="checkbox" onclick="updateServerTimelineHours()" />Log-X</label>
1118 - </div>
1119 - </div>
1120 - <canvas id=serverMainStats style=""></canvas>
1121 - </div>
1122 - <div id=p41 style="display:none">
1123 - <div id="p41title"><h1>My Server Tracing</h1></div>
1124 - <div class="areaHead">
1125 - <div class="toright2">
1126 - Show
1127 - <select id=p41limitdropdown onchange=displayServerTrace()>
1128 - <option value=100>Last 100</option>
1129 - <option value=250>Last 250</option>
1130 - <option value=500>Last 500</option>
1131 - <option value=1000>Last 1000</option>
1132 - </select>
1133 - <input value="Clear" type="button" onclick="clearServerTracing()" />
1134 - <img src=images/link4.png height=10 width=10 title="Download trace (.csv)" style=cursor:pointer onclick=p41downloadServerTrace()>&nbsp;
1135 - </div>
1136 - <div>
1137 - <input value="Tracing" type="button" onclick="setServerTracing()" />
1138 - <span id="p41traceStatus">None</span>
1139 - </div>
1140 - </div>
1141 - <div id=p41events style=""></div>
1142 - </div>
1143 - <div id=p42 style="display:none">
1144 - <h1>My Server Plugins</h1>
1145 - <div class="areaHead">
1146 - <div class="toright2">
1147 - </div>
1148 - <div>
1149 - <input value="Download Plugin" type="button" onclick="return pluginHandler.addPluginDlg();" />
1150 - </div>
1151 - </div>
1152 - <div id="pluginRestartNotice" class="areaHead" style="background-color:gold;display:none">
1153 - <div class="toright2">
1154 - <input value="Refresh Agent Cores" type="button" onclick="distributeCore();return false" />
1155 - </div>
1156 - <div style="padding:2px">
1157 - <div style="padding:2px"><b>Notice:</b> Plugins have been altered, this may require agent core update.</div>
1158 - </div>
1159 - </div>
1160 - <table id="p42tbl">
1161 - <tr class="DevSt"><th style="width:26px"></th><th style="width:10px"></th><th class="chName">Name</th><th class="chDescription">Description</th><th class="chSite" style="text-align:center">Link</th><th class="chVersion" style="text-align:center">Version</th><th class="chUpgradeAvail" style="text-align:center">Latest</th><th class="chStatus" style="text-align:center">Status</th><th class="chAction" style="text-align:center">Action</th><th style="width:10px"></th></tr>
1162 - </table>
1163 - <div id="pluginNoneNotice" style="width:100%;text-align:center;padding-top:10px;display:none"><i>No plugins on server.</i></div>
1164 - </div>
1165 - <div id=p43 style="display:none">
1166 - <div id="p43BackButton"><div class="backButton" tabindex=0 onclick=go(42) title="Back" onkeypress="if (event.key == 'Enter') go(42)"><div class="backButtonEx"></div></div></div>
1167 - <h1>My Server Plugins - <span id="p43title"></span></h1>
1168 - <iframe id="p43iframe" frameBorder=0 style="width:100%;height:calc(100vh - 245px);max-height:calc(100vh - 245px)"></iframe>
1169 - </div>
1170 - <div id=p50 style="display:none">
1171 - <div id="p50title"><h1>My User Groups</h1></div>
1172 - <table class="pTable">
1173 - <tr>
1174 - <td class="h1"></td>
1175 - <td class="style14">
1176 - <div style="float:right">
1177 - </div>
1178 - <div id="p50userGroupOps">
1179 - <input type=button id=UsersGroupsSelectAllButton onclick="p50usersSelectallButtonFunction()" value="Select All" />
1180 - <input type=button id=UsersGroupsGroupActionButton disabled="disabled" value="Group Action" onclick=p50usersGroupActionFunction() />
1181 - <input id=NewUserGroupButton type=button onclick=showCreateUserGroupDialog(1) value="New Group..." />
1182 - <input id=DuplicateUserGroupButton type=button style=display:none onclick=showCreateUserGroupDialog(2) value="Duplicate Group..." />
1183 - </div>
1184 - </td>
1185 - <td class="h2"></td>
1186 - </tr>
1187 - </table>
1188 - <div id="p50groups"></div>
1189 - </div>
1190 - <div id=p51 style="display:none">
1191 - <div id="p51title">
1192 - <div style="float:left"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
1193 - <h1>User Group - <span id=p51groupName></span></h1>
1194 - </div>
1195 - <div id="p51info" style="overflow-y:auto">
1196 - <table style="width:100%" cellpadding="0" cellspacing="0">
1197 - <tr>
1198 - <td style=width:auto valign=top>
1199 - <div id=p51group></div>
1200 - </td>
1201 - <td style=width:20px></td>
1202 - <td style=width:200px valign=top>
1203 - <picture id=MainUserImage style=border-width:0px;height:200px;width:200px;float:right>
1204 - <source type="image/webp" width=200 height=200 srcset="images/webp/group-256.webp" />
1205 - <img alt="" width=200 height=200 src=images/group-256.png />
1206 - </picture>
1207 - <div style="width:100%;text-align:center"><strong><span id=MainUserState></span></strong></div>
1208 - </td>
1209 - </tr>
1210 - </table>
1211 - <div id=p51group2></div>
1212 - <br />
1213 - </div>
1214 - </div>
1215 - <div id=p52 style="display:none">
1216 - <div id="p52title">
1217 - <h1>My User Recordings</h1>
1218 - </div>
1219 - <table class="pTable">
1220 - <tr>
1221 - <td class="h1"></td>
1222 - <td class="style14">
1223 - <div style="float:right">
1224 - <input type=button onclick=openRecodringPlayer() value="Open Player..." />
1225 - </div>
1226 - <div>
1227 - <input type=button onclick=refreshRecodings() value="Refresh" />
1228 - </div>
1229 - </td>
1230 - <td class="h2"></td>
1231 - </tr>
1232 - </table>
1233 - <div id=p52recordings style="overflow-y:auto"></div>
1234 - </div>
1235 - <div id=p60 style="display:none">
1236 - <div id="p60title">
1237 - <h1>My Reports</h1>
1238 - </div>
1239 - <table class="pTable">
1240 - <tr>
1241 - <td class="h1"></td>
1242 - <td class="style14">
1243 - <div style="float:right;line-height:22px">
1244 - <a id="p60downloadReportDiv" style="display:none" href=# onclick=p60downloadReport()><img src=images/link4.png height=10 width=10 title="Download Report" style=cursor:pointer></a>&nbsp;
1245 - </div>
1246 - <div>
1247 - <input type=button onclick=generateReportDialog() value="Generate Report..." />
1248 - </div>
1249 - </td>
1250 - <td class="h2"></td>
1251 - </tr>
1252 - </table>
1253 - <div id=p60report style="overflow-y:auto"></div>
1254 - </div>
1255 - <br id="column_l_bottomgap" />
1256 - </div>
1257 - <div id="footer">
1258 - <div class="footer1">{{{footer}}}</div>
1259 - <div class="footer2">
1260 - <a id="verifyEmailId2" style="display:none" href=# onclick="account_showVerifyEmail()">Verify Email</a>
1261 - &nbsp;<a id="termsLinkFooter" href=terms>Terms &amp; Privacy</a>
1262 - </div>
1263 - </div>
1264 - <div id=dialog class="noselect" style="display:none">
1265 - <div id=dialogHeader>
1266 - <div tabindex=0 id=id_dialogclose onclick=setDialogMode() onkeypress="if (event.key == 'Enter') setDialogMode()">&#x2716;</div>
1267 - <div id=id_dialogtitle></div>
1268 - </div>
1269 - <div id=dialogBody>
1270 - <div id=dialog1>
1271 - <div id=id_dialogMessage style=""></div>
1272 - </div>
1273 - <div id=dialog2 style="">
1274 - <div id=id_dialogOptions></div>
1275 - </div>
1276 - <div id=dialog3 style="">
1277 - <div id=d3upload>
1278 - <div>File Selection</div>
1279 - <select id=d3uploadMode onchange=d3modechange()>
1280 - <option value=1>Local file upload</option>
1281 - <option value=2>Server file selection</option>
1282 - </select>
1283 - </div>
1284 - <div id=d3localmode style="display:none">
1285 - <div>Upload File</div>
1286 - <form id=d3localmodeform method=post enctype=multipart/form-data action=uploadfile.ashx target=fileUploadFrame>
1287 - <input type=text id=d3auth name=auth style="display:none" />
1288 - <input type=text id=d3filter name=filter style="display:none" />
1289 - <input type=text id=d3attrib name=attrib style="display:none" />
1290 - <input type=file id=d3localFile name=files onchange=d3setActions() />
1291 - <input type=submit id=d3submit style="display:none" />
1292 - </form>
1293 - </div>
1294 - <div id=d3servermode>
1295 - <div id=d3serveraction valign=bottom>
1296 - <input type=button id=p3FolderUp disabled="disabled" onclick=d3folderup() value="Up" />&nbsp;<span id=p3CurrentFolder></span>
1297 - </div>
1298 - <div id=d3serverfiles></div>
1299 - </div>
1300 - </div>
1301 - <div id=dialog4 style="">
1302 - <input id="d4WrapButton" type="button" value="Wrap On" onclick="d4ToggleWrap()" />
1303 - <input id="d4SizeButton" type="button" value="Small" onclick="d4ToggleSize()" />
1304 - <input id="d4EncodingButton" type="button" value="Raw" onclick="d4ToggleEncoding()" />
1305 - <textarea id=d4editorarea autocomplete="off" style="height:calc(100vh - 286px);width:100%;overflow:scroll;resize:none;white-space:pre"></textarea>
1306 - </div>
1307 - <div id=dialog7 style="">
1308 - <div class="dtab">
1309 - <button id="td7meshkvm" class="tablinks" onclick="changeDesktopSettingsTab(event, 'd7meshkvm')">Agent</button>
1310 - <button id="td7rdpkvm" class="tablinks" onclick="changeDesktopSettingsTab(event, 'd7rdpkvm')">RDP</button>
1311 - <button id="td7amtkvm" class="tablinks" onclick="changeDesktopSettingsTab(event, 'd7amtkvm')">Intel&reg; AMT</button>
1312 - </div>
1313 - <div id="d7meshkvm" class="tabcontent">
1314 - <!--<h4>Agent Remote Desktop</h4>-->
1315 - <div style="margin-top:8px">
1316 - <div>Quality</div>
1317 - <select id="d7bitmapquality" dir="rtl"></select>
1318 - </div>
1319 - <div>
1320 - <div>Scaling</div>
1321 - <select id="d7bitmapscaling" style="" dir="rtl">
1322 - <option selected=selected value=1024>100%</option>
1323 - <option value=896>87.5%</option>
1324 - <option value=768>75%</option>
1325 - <option value=640>62.5%</option>
1326 - <option value=512>50%</option>
1327 - <option value=384>37.5%</option>
1328 - <option value=256>25%</option>
1329 - <option value=128>12.5%</option>
1330 - </select>
1331 - </div>
1332 - <div>
1333 - <div>Frame rate</div>
1334 - <select id="d7framelimiter" dir="rtl">
1335 - <option selected=selected value=50>Fast</option>
1336 - <option value=100>Medium</option>
1337 - <option value=400>Slow</option>
1338 - <option value=1000>Very slow</option>
1339 - </select>
1340 - </div>
1341 - <div id="d7desktopOtherSettings">
1342 - <div>Other Settings</div>
1343 - <div id="d7otherset2" style="display:block">
1344 - <label style="display:block"><input type="checkbox" id="d7deskSwapMouse" />Swap Mouse Buttons</label>
1345 - <label style="display:block"><input type="checkbox" id="d7deskrmw" />Reverse Mouse Wheel</label>
1346 - <label style="display:block"><input type="checkbox" id="d7deskRemoteKeyMap" />Use Remote Keyboard Map</label>
1347 - <label style="display:block" id="d7deskAutoClipboardLabel"><input type="checkbox" id="d7deskAutoClipboard" />Automatic Clipboard</label>
1348 - <label style="display:block" id="d7deskAutoLockLabel"><input type="checkbox" id="d7deskAutoLock" />Lock on Disconnect</label>
1349 - </div>
1350 - </div>
1351 - </div>
1352 - <div id="d7amtkvm" class="tabcontent">
1353 - <!--<h4>Intel&reg; AMT Hardware KVM</h4>-->
1354 - <div style="margin-top:8px">
1355 - <div>Image Encoding</div>
1356 - <select id="d7desktopmode">
1357 - <option value="1">RLE8, Fastest</option>
1358 - <option value="2">RLE16, Recommended</option>
1359 - <option value="3">RAW8, Slow</option>
1360 - <option value="4">RAW16, Very Slow</option>
1361 - </select>
1362 - </div>
1363 - <div>
1364 - <div>Other Settings</div>
1365 - <div id="d7otherset" style="display:block">
1366 - <label style="display:block"><input type="checkbox" id="d7showfocus" />Show Focus Tool</label>
1367 - <label style="display:block"><input type="checkbox" id="d7showcursor" />Show Local Mouse Cursor</label>
1368 - <label style="display:block"><input type="checkbox" id="d7localKeyMap" />Local Keyboard Map</label>
1369 - <label style="display:block"><input type="checkbox" id="d7kvmrmw" />Reverse Mouse Wheel</label>
1370 - </div>
1371 - </div>
1372 - </div>
1373 - <div id="d7rdpkvm" class="tabcontent">
1374 - <!--<h4>Remote Desktop Protocol</h4>-->
1375 - <div style="margin-top:8px">
1376 - <div>Display Size</div>
1377 - <select id="d7rdpsize">
1378 - <option value="canvas">Canvas Size</option>
1379 - <option value="browser">Browser Size</option>
1380 - <option value="screen">Screen Size</option>
1381 - <option value="640x480">640×480</option>
1382 - <option value="1024x768">1024×768</option>
1383 - <option value="1280x800">1280×800</option>
1384 - <option value="1440x900">1440×900</option>
1385 - <option value="1600x900">1600×900</option>
1386 - <option value="1680x1050">1680×1050</option>
1387 - <option value="1920x1080">1920×1080</option>
1388 - </select>
1389 - </div>
1390 - <div>
1391 - <div>Options</div>
1392 - <div id="d7rdpflags" style="display:block">
1393 - <label style="display:block"><input type="checkbox" id="d7rdp1" />Disable Wallpaper</label>
1394 - <label style="display:block"><input type="checkbox" id="d7rdp2" />Disable Full Window Drag</label>
1395 - <label style="display:block"><input type="checkbox" id="d7rdp3" />Disable Menu Animations</label>
1396 - <label style="display:block"><input type="checkbox" id="d7rdp4" />Disable Theming</label>
1397 - <label style="display:block"><input type="checkbox" id="d7rdp6" />Disable Cursor Shadow</label>
1398 - <label style="display:block"><input type="checkbox" id="d7rdp7" />Disable Cursor Settings</label>
1399 - <label style="display:block"><input type="checkbox" id="d7rdp8" />Enable Font Smooting</label>
1400 - <label style="display:block"><input type="checkbox" id="d7rdp9" />Enable Desktop Composision</label>
1401 - <label style="display:block"><input type="checkbox" id="d7rdpclip" />Automatic Clipboard</label>
1402 - <label style="display:block"><input type="checkbox" id="d7rdpsmb" />Swap Mouse Buttons</label>
1403 - <label style="display:block"><input type="checkbox" id="d7rdprmw" />Reverse Mouse Wheel</label>
1404 - </div>
1405 - </div>
1406 - </div>
1407 - </div>
1408 - </div>
1409 - <div id="idx_dlgButtonBar">
1410 - <input id="idx_dlgCancelButton" type="button" value="Cancel" style="" onclick="dialogclose(0)" />
1411 - <input id="idx_dlgOkButton" type="button" value="OK" style="" onclick="dialogclose(1)" />
1412 - <div><input id="idx_dlgDeleteButton" type="button" value="Delete" style="display:none" onclick="dialogclose(2)" /></div>
1413 - <div id="idx_dlgMoreButtons">
1414 - <a href=# id=idx_dlgMoreButtons1 style=cursor:pointer;color:gray;text-decoration:none title="Toggle advanced options" onclick=MoreToggle(true)>&#x25BC;</a>
1415 - <a href=# id=idx_dlgMoreButtons2 style=cursor:pointer;color:gray;text-decoration:none title="Toggle advanced options" onclick=MoreToggle(false)>&#x25B2;</a>
1416 - </div>
1417 - </div>
1418 - </div>
1419 - <iframe name="fileUploadFrame" style="display:none"></iframe>
1420 - <form style="display:none" method=post action=uploadfile.ashx enctype=multipart/form-data target=fileUploadFrame><input id=p5fileDragName name="name" /><input id=p5fileDragAuthCookie name="auth" /><input id=p5fileDragSize name="size" /><input id=p5fileDragType name="type" /><input id=p5fileDragData name="data" /><input id=p5fileDragLink name="link" /><input type=submit id=p5loginSubmit2 style="display:none" /></form>
1421 - <form style="display:none" method=post action=uploadnodefile.ashx enctype=multipart/form-data target=fileUploadFrame><input id=p13fileDragName name="name" /><input id=p13fileDragSize name="size" /><input id=p13fileDragType name="type" /><input id=p13fileDragData name="data" /><input id=p13fileDragLink name="link" /><input type=submit id=p13loginSubmit2 style="display:none" /></form>
1422 - <audio id="chimes"><source src="sounds/chimes.mp3" type="audio/mp3" /></audio>
1423 - <iframe style=display:none name="fileDownloadFrame"></iframe>
1424 - </div>
1425 - <script type="text/javascript">
1426 - 'use strict';
1427 - var random = '{{{randomlength}}}' // Random length string for BREACH mitigation
1428 -
1429 - // Process server-side web state
1430 - var webState = '{{{webstate}}}';
1431 - if (webState != '') { webState = JSON.parse(decodeURIComponent(webState)); }
1432 - if ((webState == null) || (typeof webState != 'object')) { webState = {}; }
1433 - for (var i in webState) { if (i != 'desktopsettings') { try { localStorage.setItem(i, webState[i]); } catch (ex) {} } }
1434 - if (!webState.loctag) { try { delete localStorage.removeItem('loctag'); } catch (ex) { } }
1435 -
1436 - var args, urlargs;
1437 - var autoReconnect = true;
1438 - var powerStatetable = ['', "Powered", "Sleep", "Sleep", "Sleep", "Hibernating", "Power off", "Present"];
1439 - var StatusStrs = ["Disconnected", "Connecting...", "Setup...", "Connected", "Intel&reg; AMT Connected"];
1440 - var agentsStr = ["Unknown", "Windows 32bit console", "Windows 64bit console", "Windows 32bit service", "Windows 64bit service", "Linux 32bit", "Linux 64bit", "MIPS", "XENx86", "Android", "Linux ARM", "macOS x86-32bit", "Android x86", "PogoPlug ARM", "Android", "Linux Poky x86-32bit", "macOS x86-64bit", "ChromeOS", "Linux Poky x86-64bit", "Linux NoKVM x86-32bit", "Linux NoKVM x86-64bit", "Windows MinCore console", "Windows MinCore service", "NodeJS", "ARM-Linaro", "ARMv6l / ARMv7l", "ARMv8 64bit", "ARMv6l / ARMv7l / NoKVM", "MIPS24KC (OpenWRT)", "Apple Silicon", "FreeBSD x86-64", "Unknown", "Linux ARM 64 bit (glibc/2.24 NOKVM)", "Alpine Linux x86 64 Bit (MUSL)", "Assistant (Windows)", "Armada370 - ARM32/HF (libc/2.26)", "OpenWRT x86-64", "OpenBSD x86-64", "Unknown", "Unknown", "MIPSEL24KC (OpenWRT)", "ARMADA/CORTEX-A53/MUSL (OpenWRT)", "Windows ARM 64bit console", "Windows ARM 64bit service"];
1441 - var agentsStrNoAgent = ['', '', '', '', "Windows", '', "Linux", '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', "Apple"];
1442 - var sort = 0;
1443 - var searchFocus = 0;
1444 - var mapSearchFocus = 0;
1445 - var userSearchFocus = 0;
1446 - var consoleFocus = 0;
1447 - var showRealNames = false;
1448 - var meshserver = null;
1449 - var meshes = {};
1450 - var loginTokens = {};
1451 - var meshcount = 0;
1452 - var nodes = null;
1453 - var usergroups = null;
1454 - var filetree = {};
1455 - var userinfo = null;
1456 - var serverinfo = null;
1457 - var events = [];
1458 - var users = null;
1459 - var wssessions = null;
1460 - var stars = {}; // Devices that have been "stared" by the user.
1461 - var nodeShortIdent = 0;
1462 - var desktop;
1463 - var desktopsettings = { encoding: 2, showfocus: false, showmouse: true, showcad: true, quality: 40, scaling: 1024, framerate: 50, localkeymap: false, swapmouse: false, remotekeymap: false, autoclipboard: false, autolock: false };
1464 - var multidesktopsettings = { quality: 20, scaling: 128, framerate: 1000 };
1465 - var terminal;
1466 - var files;
1467 - var debugLevel = parseInt('{{{debuglevel}}}');
1468 - var features = parseInt('{{{features}}}');
1469 - var features2 = parseInt('{{{features2}}}');
1470 - var sessionTime = parseInt('{{{sessiontime}}}');
1471 - var webRelayPort = parseInt('{{{webRelayPort}}}');
1472 - var webRelayDns = '{{{webRelayDns}}}';
1473 - var sessionRefreshTimer = null;
1474 - var domain = '{{{domain}}}';
1475 - var domainUrl = '{{{domainurl}}}';
1476 - var authCookie = '{{{authCookie}}}';
1477 - var authRelayCookie = '{{{authRelayCookie}}}';
1478 - var logoutControls = JSON.parse(decodeURIComponent('{{{logoutControls}}}'));
1479 - var authCookieRenewTimer = null;
1480 - var multiDesktop = {};
1481 - var serverPublicNamePort = '{{{serverDnsName}}}:{{{serverPublicPort}}}';
1482 - var amtScanResults = null;
1483 - var debugmode = 0;
1484 - var windowsBrowser = detectWindowsBrowser();
1485 - var attemptWebRTC = ((features & 128) != 0);
1486 - var passRequirements = '{{{passRequirements}}}';
1487 - if (passRequirements != '') { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); }
1488 - var customui = '{{{customui}}}';
1489 - if (customui != '') { customui = JSON.parse(decodeURIComponent(customui)); } else { customui = null; }
1490 - var deskAspectRatio = 0;
1491 - try { deskAspectRatio = parseInt(getstore('deskAspectRatio', '0')); } catch (ex) { }
1492 - var uiMode = parseInt(getstore('uiMode', 1));
1493 - var webPageStackMenu = false;
1494 - var webPageFullScreen = true;
1495 - var nightMode = setNightMode();
1496 - var footerBar = (getstore('footerBar', '1') == '1');
1497 - var sessionActivity = Date.now();
1498 - var updateSessionTimer = null;
1499 - var pluginHandlerBuilder = {{{pluginHandler}}};
1500 - var pluginHandler = null;
1501 - if (pluginHandlerBuilder != null) { pluginHandler = new pluginHandlerBuilder(); }
1502 - var installedPluginList = null;
1503 - var goBackStack = [];
1504 - var CollapsedGroups = {};
1505 - try { CollapsedGroups = JSON.parse(getstore('_collapse', '{}')); } catch(ex) {}
1506 - var deviceViewSettings = {};
1507 - try { deviceViewSettings = JSON.parse(getstore('_deviceViewSettings', '{}')); } catch(ex) {}
1508 - var userViewSettings = {};
1509 - try { userViewSettings = JSON.parse(getstore('_usersViewSettings', '{}')); } catch(ex) {}
1510 - var xterm = null;
1511 - var xtermfit = null;
1512 - var xtermResizeTimer = null;
1513 - var miscState = {};
1514 - var checkedNodeids = {};
1515 - var deskKeyboardShortcuts = [];
1516 - var deskKeyboardStrings = [];
1517 - var deskLastClipboardSent = null;
1518 - var requestedLastConnects = false;
1519 - var devicePagingState = null;
1520 -
1521 - // Console Message Display Timers
1522 - var p11DeskConsoleMsgTimer = null;
1523 - var p12TermConsoleMsgTimer = null;
1524 - var p13FilesConsoleMsgTimer = null;
1525 -
1526 - /*
1527 - // Check browser dark mode preference
1528 - var prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
1529 - var prefersLightScheme = window.matchMedia('(prefers-color-scheme: light)').matches;
1530 - console.log(prefersDarkScheme, prefersLightScheme);
1531 - var mql = window.matchMedia('(prefers-color-scheme: dark)');
1532 - mql.addEventListener('change', function() { console.log('Dark Change'); });
1533 - */
1534 -
1535 - // Check if WebP is supported
1536 - var webpSupport = false;
1537 - check_webp_feature('lossy', function (f, x) { webpSupport = x; });
1538 -
1539 - function startup() {
1540 - if ((features & 32) == 0) {
1541 - // Guard against other site's top frames (web bugs).
1542 - var loc = null;
1543 - try { loc = top.location.toString().toLowerCase(); } catch (e) { }
1544 - if (top != self && (loc == null || top.active == false)) { top.location = self.location; return; }
1545 - }
1546 -
1547 - // Fetch URL arguments & do sanitation
1548 - urlargs = parseUriArgs();
1549 - if (urlargs.key != null) { urlargs.key = "" + urlargs.key; }
1550 - if (urlargs.key && (isAlphaNumeric(urlargs.key) == false)) { delete urlargs.key; }
1551 - if (urlargs.locale && (isAlphaNumeric(urlargs.locale) == false)) { delete urlargs.locale; }
1552 - delete urlargs.user;
1553 - delete urlargs.pass;
1554 - delete urlargs.viewmode;
1555 - delete urlargs.gotonode;
1556 - delete urlargs.gotodevicename;
1557 - delete urlargs.gotomesh;
1558 - delete urlargs.gotouser;
1559 - delete urlargs.gotougrp;
1560 -
1561 - // Fix links if a loginKey is used
1562 - if (urlargs.key) { Q('termsLinkFooter').href += '?key=' + urlargs.key; }
1563 -
1564 - // Check if we are in debug mode
1565 - args = parseUriArgs();
1566 - if (args.key && (isAlphaNumeric(args.key) == false)) { delete args.key; }
1567 - if (args.locale && (isAlphaNumeric(args.locale) == false)) { delete args.locale; }
1568 -
1569 - if (!args.locale) { var x = getstore('loctag', 0); if ((x != null) && (x != '*')) { args.locale = x; } }
1570 - debugmode = args.debug;
1571 -
1572 - //attemptWebRTC = false; // For now, default WebRTC off unless we set it in the URL.
1573 - if (args.webrtc != null) { attemptWebRTC = (args.webrtc == 1); }
1574 -
1575 - QV('p13AutoConnect', debugmode); // Files
1576 - QV('autoconnectbutton2', debugmode); // Terminal
1577 - QV('autoconnectbutton1', debugmode); // Desktop
1578 - //QV('DeskClip', debugmode); // Clipboard feature, not completed so show in in debug mode only.
1579 -
1580 - if (nightMode) { QC('body').add('night'); QS('body')['background-color'] = '#000'; }
1581 - toggleFullScreen();
1582 -
1583 - // Setup stared devices
1584 - try { stars = JSON.parse(getstore('stars', '{}')); } catch (ex) {}
1585 -
1586 - // Setup page visuals
1587 - var hide = 0;
1588 - var globalHide = parseInt('{{{hide}}}');
1589 - if (globalHide || args.hide) {
1590 - if (args.hide) { hide = parseInt(args.hide); }
1591 - if (globalHide) { hide = (hide | globalHide); }
1592 - }
1593 - args.hide = hide;
1594 - QV('uiViewButton5', !(args.hide & 4)); // Hide the footer toggle button if footer is hidden anyway.
1595 - adjustPanels();
1596 -
1597 - // Setup logout control
1598 - var logoutControl = '';
1599 - if (logoutControls) {
1600 - if (logoutControls.name != null) { logoutControl = ('<span onclick=go(2) CLASS="LogoffLinkColor" style="cursor:pointer">' + format("Welcome {0}.", logoutControls.name) + '</span>'); }
1601 - if (logoutControls.logoutUrl != null) { logoutControl += format(' <a href="' + logoutControls.logoutUrl + '" CLASS="LogoffLinkColor">' + "Logout" + '</a>'); }
1602 - }
1603 - if (args.hide & 1) { QH('logoutControlSpan2', logoutControl); } else { QH('logoutControlSpan', logoutControl); }
1604 -
1605 - // Setup the context menu
1606 - document.onclick = function (e) { hideContextMenu(); }
1607 - document.onkeypress = ondockeypress;
1608 - document.onkeydown = ondockeydown;
1609 - document.onkeyup = ondockeyup;
1610 - //window.addEventListener('focus', ondocfocus, false);
1611 - window.addEventListener('blur', ondocblur, false);
1612 - window.onresize = function () {
1613 - hideContextMenu();
1614 - mainUpdate(512);
1615 - browserfullscreen = isBrowserFullscreen();
1616 - QV('DeskESC', browserfullscreen);
1617 - if ((xtermfit != null) && (xxcurrentView == 12)) { xtermfit.fit(); }
1618 - }
1619 - setTimeout(function() { mainUpdate(512); }, 200);
1620 -
1621 - // Connect to the mesh server
1622 - meshserver = MeshServerCreateControl(domainUrl);
1623 - meshserver.onStateChanged = onStateChanged;
1624 - meshserver.onMessage = onMessage;
1625 - meshserver.trace = args.trace;
1626 - meshserver.Start();
1627 -
1628 - // Setup page controls
1629 - Q('sortselect').selectedIndex = sort = getstore('sort', 0);
1630 - Q('sizeselect').selectedIndex = getstore('viewsize', 1);
1631 - Q('KvmSearchInput').value = Q('SearchInput').value = getstore('_search', '');
1632 - showRealNames = (getstore('showRealNames', 0) == 1);
1633 - Q('RealNameCheckBox').checked = showRealNames;
1634 - Q('DevFilterSelect').value = getstore('devFilterSelect', 0);
1635 - Q('viewselect').value = getstore('deviceView', 1);
1636 - Q('DeskControl').checked = (getstore('DeskControl', 1) == 1);
1637 - QV('accountChangeEmailAddressSpan', (features & 0x200000) == 0);
1638 -
1639 - // Display the page devices
1640 - mainUpdate(3)
1641 - for (var j = 1; j < 5; j++) { Q('devViewButton' + j).classList.remove('viewSelectorSel'); }
1642 - Q('devViewButton' + Q('viewselect').value).classList.add('viewSelectorSel');
1643 -
1644 - // Setup upload drag & drop for server files
1645 - Q('p5filetable').addEventListener('drop', p5fileDragDrop, false);
1646 - Q('p5filetable').addEventListener('dragover', p5fileDragOver, false);
1647 - Q('p5filetable').addEventListener('dragleave', p5fileDragLeave, false);
1648 - //Q('p5fileCatchAllInput').addEventListener('drop', p5fileDragDrop, false);
1649 - //Q('p5fileCatchAllInput').addEventListener('dragover', p5fileDragOver, false);
1650 - //Q('p5fileCatchAllInput').addEventListener('dragleave', p5fileDragLeave, false);
1651 -
1652 - // Setup drag & drop for device desktop
1653 - Q('deskarea0').addEventListener('drop', p11fileDragDrop, false);
1654 - Q('deskarea0').addEventListener('dragover', p11fileDragOver, false);
1655 - Q('deskarea0').addEventListener('dragleave', p11fileDragLeave, false);
1656 -
1657 - // Setup upload drag & drop for device files
1658 - Q('p13filetable').addEventListener('drop', p13fileDragDrop, false);
1659 - Q('p13filetable').addEventListener('dragover', p13fileDragOver, false);
1660 - Q('p13filetable').addEventListener('dragleave', p13fileDragLeave, false);
1661 -
1662 - // Timeline update interval
1663 - setInterval(updateDeviceTimeline, 120000); // Check every 2 minutes
1664 -
1665 - // Load desktop settings
1666 - var t = null;
1667 - try { t = localStorage.getItem('desktopsettings'); } catch (ex) {}
1668 - if (t != null) { desktopsettings = JSON.parse(t); }
1669 - t = null;
1670 - try { t = localStorage.getItem('multidesktopsettings'); } catch (ex) {}
1671 - if (t != null) { multidesktopsettings = JSON.parse(t); }
1672 - applyDesktopSettings();
1673 -
1674 - // Terminal special keys
1675 - var x = '';
1676 - for (var c = 1; c < 27; c++) x += '<option value=\'' + c + '\'>' + "Ctrl" + '-' + String.fromCharCode(64 + c) + ' (' + c + ')</option>';
1677 - QH('specialkeylist', x);
1678 -
1679 - // Setup server stats panels
1680 - setupGeneralServerStats();
1681 - setupServerTimelineStats();
1682 -
1683 - // Setup the user interface in the right mode
1684 - userInterfaceSelectMenu();
1685 -
1686 - // Setup Mesh summary panel
1687 - setupMeshSummaryStats();
1688 -
1689 - // If SSPI or LDAP authentication not used, allow batch account creation.
1690 - QV('p4UserBatchCreate', (features & 0x00080000) == 0);
1691 -
1692 - // Set the file editor
1693 - d4EditWrapVal = getstore('editorWrap', 0);
1694 - d4EditSizeVal = getstore('editorSize', 0);
1695 - d4EditEncodingVal = getstore('editorEncoding', 0);
1696 - d4ToggleWrap(true);
1697 - d4ToggleSize(true);
1698 - d4ToggleEncoding(true);
1699 - if (pluginHandler != null) pluginHandler.callHook('onWebUIStartupEnd');
1700 -
1701 - // Deleted non-english style and fix all topbar titles
1702 - if ((['en','ko','ja','zh-chs','zh-cht'].indexOf('{{{lang}}}') == -1) && ('{{{lang}}}'.toLowerCase() != '')) { QC('body').add('nonenglish'); }
1703 - var elements = document.getElementsByClassName('topbar_td');
1704 - for (var i in elements) { if (elements[i].innerHTML) { elements[i].innerHTML = elements[i].innerHTML.split(' ').join('&nbsp;'); } }
1705 -
1706 - // Custom UI
1707 - if (customui != null) {
1708 - if (customui.devicesbarbuttons) {
1709 - var x = '';
1710 - for (var i in customui.devicesbarbuttons) {
1711 - var disabled = ((customui.devicesbarbuttons[i].selection == 'one') || (customui.devicesbarbuttons[i].selection == 'many'))?'disabled':'';
1712 - x += '<input id="cui:' + i + '" type="button" value="' + customui.devicesbarbuttons[i].name + '" ' + disabled + ' onclick=customUIAction(event,"devicesbarbuttons") />';
1713 - }
1714 - QH('devsCustomUIBar', x);
1715 - }
1716 - if (customui.desktopbuttons) {
1717 - var x = '';
1718 - for (var i in customui.desktopbuttons) { x += '<input id="cui:' + i + '" type="button" value="' + customui.desktopbuttons[i].name + '" style="float:left" onclick=customUIAction(event,"desktopbuttons") />'; }
1719 - QH('desktopCustomUiButtons', x);
1720 - }
1721 - if (customui.terminalbuttons) {
1722 - var x = '';
1723 - for (var i in customui.terminalbuttons) { x += '<input id="cui:' + i + '" type="button" value="' + customui.terminalbuttons[i].name + '" style="float:left" onclick=customUIAction(event,"terminalbuttons") />'; }
1724 - QH('terminalCustomUiButtons', x);
1725 - }
1726 - if (customui.filesbuttons) {
1727 - var x = '';
1728 - for (var i in customui.filesbuttons) { x += '<input id="cui:' + i + '" type="button" value="' + customui.filesbuttons[i].name + '" style="float:left" onclick=customUIAction(event,"filesbuttons") />'; }
1729 - QH('filesCustomUiButtons', x);
1730 - }
1731 - }
1732 -
1733 - // Session Refresh Timer
1734 - if (sessionTime >= 10) { sessionRefreshTimer = setTimeout(refreshCookieSession, Math.round((sessionTime * 60000) * 0.8)); }
1735 -
1736 - // Set the user's desktop shortcut keys
1737 - deskKeyboardShortcuts = [];
1738 - var deskKeyboardShortcutsStr = getstore('deskKeyShortcuts', '0x0A002E,0x100000,0x100028,0x100026,0x10004C,0x10004D,0x11004D,0x100052,0x020073,0x080057,0x020009,0x100025,0x100027').split(',');
1739 - for (var i in deskKeyboardShortcutsStr) { deskKeyboardShortcuts.push(parseInt(deskKeyboardShortcutsStr[i])); }
1740 - updateDeskShortcutKeys();
1741 -
1742 - // Set the user's desktop strings
1743 - try { deskKeyboardStrings = JSON.parse(getstore('deskStrings', '[]')); } catch (ex) {}
1744 - updateDesktopStrings();
1745 -
1746 - // Override the collapse button text
1747 - updateCollapseAllButton();
1748 -
1749 - // Make the dialog box movable
1750 - dialogBoxDrag();
1751 -
1752 - // Fix links
1753 - if (urlargs.key) { Q('p6backuplink').href += '?key=' + urlargs.key; }
1754 -
1755 - // Hide night mode button if needed
1756 - QV('uiViewButton4', (features2 & 0x00300000) == 0);
1757 -
1758 - // Fix HTML words that in english have two or more meanings
1759 - Q('DeskType').value = multiTranslate("[KeyboardTyping]|Type");
1760 - }
1761 -
1762 - function refreshCookieSession() {
1763 - var xdr = null;
1764 - try { xdr = new XDomainRequest(); } catch (e) { }
1765 - if (!xdr) xdr = new XMLHttpRequest();
1766 - xdr.open('GET', window.location.origin + domainUrl + 'refresh.ashx');
1767 - xdr.timeout = 15000;
1768 - xdr.onload = function () { sessionRefreshTimer = setTimeout(refreshCookieSession, Math.round((sessionTime * 60000) * 0.8)); };
1769 - xdr.onerror = xdr.ontimeout = function () { sessionRefreshTimer = null; };
1770 - xdr.send();
1771 - }
1772 -
1773 - // Generic handling of custom actions
1774 - function customUIAction(e, section) {
1775 - var id = e.srcElement.id;
1776 - if (id.startsWith('cui:') == false) return;
1777 - var info = customui[section][id.substring(4)];
1778 - if (info == null) return;
1779 - if (section == 'devicesbarbuttons') {
1780 - var elements = document.getElementsByClassName('DeviceCheckbox'), selectedDevices = [];
1781 - for (var i = 0; i < elements.length; i++) { if (elements[i].checked === true) { selectedDevices.push(elements[i].defaultValue.substring(6)); } }
1782 - if (typeof info.action == 'string') {
1783 - if (info.action == 'event') { meshserver.send({ action: 'uicustomevent', section: section, element: id.substring(4), selectedDevices: selectedDevices, logmsg: info.logmsg }); }
1784 - if (info.action.startsWith('dialog:')) { showCustomUiDialog(info.action.substring(7), { section: section, element: id.substring(4), selectedDevices: selectedDevices }); }
1785 - if (info.deselectdevices) { for (var i = 0; i < elements.length; i++) { elements[i].checked = false; } checkedNodeids = {}; p1updateInfo(); }
1786 - }
1787 - }
1788 - if ((section == 'devicebuttons') || (section == 'desktopbuttons') || (section == 'terminalbuttons') || (section == 'filesbuttons')) {
1789 - var selectedDevices = [ currentNode._id ];
1790 - if (typeof info.action == 'string') {
1791 - if (info.action == 'event') { meshserver.send({ action: 'uicustomevent', section: section, element: id.substring(4), selectedDevices: selectedDevices, logmsg: info.logmsg }); }
1792 - if (info.action.startsWith('dialog:')) { showCustomUiDialog(info.action.substring(7), { section: section, element: id.substring(4), selectedDevices: selectedDevices }); }
1793 - }
1794 - }
1795 - }
1796 -
1797 - // Display a generic custom UI dialog
1798 - function showCustomUiDialog(name, tag) {
1799 - if ((customui == null) || (customui.dialogs == null) || (typeof customui.dialogs[name] != 'object')) return;
1800 - var x = '', dialog = customui.dialogs[name], buttons = 3;
1801 - if (typeof dialog.text == 'string') { x += '<div style=margin-bottom:8px>' + dialog.text + '</div>'; }
1802 - if (typeof dialog.buttons == 'number') { buttons = dialog.buttons; }
1803 - if (typeof dialog.elements == 'object') {
1804 - for (var i in dialog.elements) {
1805 - var elem = dialog.elements[i];
1806 - if (elem.type == 'text') { x += addHtmlValue(elem.name, '<input id=cui:' + i + ' style=width:230px autocomplete=off />'); }
1807 - if (elem.type == 'textarea') {
1808 - if (elem.name == null) {
1809 - x += '<textarea id=cui:' + i + ' style=width:356px;resize:none;height:100px autocomplete=off></textarea>';
1810 - } else {
1811 - x += addHtmlValue(elem.name, '<textarea id=cui:' + i + ' style=width:230px;resize:none;height:100px autocomplete=off></textarea>');
1812 - }
1813 - }
1814 - if (elem.type == 'droplist') {
1815 - var y = '';
1816 - for (var j in elem.options) { y += '<option value="' + j + '">' + EscapeHtml(elem.options[j]) + '</option>'; }
1817 - x += addHtmlValue(elem.name, '<select id="cui:' + i + '" style=width:230px autocomplete=off />' + y + '</select>');
1818 - }
1819 - }
1820 - }
1821 - setDialogMode(2, dialog.title, buttons, showCustomUiDialogEx, x, { action: 'uicustomevent', section: 'dialogs', element: name, src: tag, values: {}, logmsg: dialog.logmsg });
1822 - }
1823 -
1824 - // Handle a generic custom UI dialog event
1825 - function showCustomUiDialogEx(b, t) {
1826 - if (b != 1) return;
1827 - var dialog = customui.dialogs[t.element];
1828 - if (typeof dialog.elements == 'object') {
1829 - for (var i in dialog.elements) {
1830 - var elem = dialog.elements[i];
1831 - if ((elem.type == 'droplist') || (elem.type == 'textarea') || (elem.type == 'text')) { t.values[i] = Q('cui:' + i).value; }
1832 - }
1833 - }
1834 - meshserver.send(t);
1835 - if (dialog.deselectdevices) {
1836 - var elements = document.getElementsByClassName('DeviceCheckbox')
1837 - for (var i = 0; i < elements.length; i++) { elements[i].checked = false; } checkedNodeids = {}; p1updateInfo();
1838 - }
1839 - }
1840 -
1841 - function adjustPanels() {
1842 - var hide = args.hide;
1843 - if (footerBar == false) { hide |= 4; }
1844 - QV('masthead', !(hide & 1));
1845 - QV('topbar', !(hide & 2));
1846 - QV('footer', !(hide & 4));
1847 - QV('p1title', !(hide & 8));
1848 - QV('p2title', !(hide & 8));
1849 - QV('p3title', !(hide & 8));
1850 - QV('p4title', !(hide & 8));
1851 - QV('p5title', !(hide & 8));
1852 - QV('p6title', !(hide & 8));
1853 - QV('p10title', !(hide & 8));
1854 - QV('p11title', !(hide & 8));
1855 - QV('p12title', !(hide & 8));
1856 - QV('p13title', !(hide & 8));
1857 - QV('p14title', !(hide & 8));
1858 - QV('p15title', !(hide & 8));
1859 - QV('p16title', !(hide & 8));
1860 - QV('p17title', !(hide & 8));
1861 - QV('p20title', !(hide & 8));
1862 - QV('p21title', !(hide & 8));
1863 - QV('p30title', !(hide & 8));
1864 - QV('p31title', !(hide & 8));
1865 - QV('p40title', !(hide & 8));
1866 - QV('p41title', !(hide & 8));
1867 - QV('p50title', !(hide & 8));
1868 - QV('p51title', !(hide & 8));
1869 - //if (hide & 16) { QV('page_leftbar', false); QS('page_content').left = '0px'; }
1870 - if (!footerBar) { QC('body').add('nofooter'); } else { QC('body').remove('nofooter'); }
1871 -
1872 - if (args.hide != 0) {
1873 - // Fix the main grid to zero-height elements we want to hide.
1874 - if (uiMode == 2) {
1875 - QS('container')['grid-template-rows'] = ((hide & 1) ? '0' : '66') + 'px fit-content(48px) auto ' + ((hide & 4) ? '0' : '45') + 'px';
1876 - QS('container')['-ms-grid-rows'] = ((hide & 1) ? '0' : '66') + 'px fit-content(48px) auto ' + ((hide & 4) ? '0' : '45') + 'px';
1877 - } else {
1878 - QS('container')['grid-template-rows'] = ((hide & 1) ? '0' : '66') + 'px ' + ((hide & 2) ? '0' : '24') + 'px auto ' + ((hide & 4) ? '0' : '45') + 'px';
1879 - QS('container')['-ms-grid-rows'] = ((hide & 1) ? '0' : '66') + 'px ' + ((hide & 2) ? '0' : '24') + 'px auto ' + ((hide & 4) ? '0' : '45') + 'px';
1880 - }
1881 - }
1882 -
1883 - // Adjust height of remote desktop, files and Intel AMT
1884 - // 1 = Top bar, 2 = Tool Bar, 4 = Bottom Bar, 8 = Tab Title
1885 - var xh = (((hide & 1) ? 0 : 66) + ((hide & 2) ? 0 : 24) + ((hide & 4) ? 0 : 45) + ((hide & 8) ? 0 : 60)); // 0 to 195
1886 - var xh2 = (uiMode > 1)?24:0;
1887 - QS('p2info')['height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
1888 - QS('p2info')['max-height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
1889 - QS('p3users')['max-height'] = 'calc(100vh - ' + (50 + xh) + 'px)'; // 124
1890 - QS('p50groups')['max-height'] = 'calc(100vh - ' + (50 + xh) + 'px)'; // 124
1891 - QS('p3events')['height'] = 'calc(100vh - ' + (50 + xh) + 'px)'; // 124
1892 - QS('p5filetable')['height'] = 'calc(100vh - ' + (99 + xh) + 'px)'; // 160
1893 - QS('p13filetable')['height'] = 'calc(100vh - ' + (127 + xh + xh2) + 'px)'; // 124
1894 - QS('serverMainStats')['height'] = 'calc(100vh - ' + (50 + xh + xh2) + 'px)'; // 110
1895 - QS('serverMainStats')['max-height'] = 'calc(100vh - ' + (50 + xh + xh2) + 'px)'; // 110
1896 - QS('xdevices')['max-height'] = 'calc(100vh - ' + (46 + xh) + 'px)'; // 124
1897 - QS('xdevicesmap')['max-height'] = 'calc(100vh - ' + (46 + xh) + 'px)'; // 124
1898 - QS('p15agentConsole')['height'] = 'calc(100vh - ' + (84 + xh + xh2) + 'px)';
1899 - QS('p15agentConsole')['max-height'] = 'calc(100vh - ' + (84 + xh + xh2) + 'px)';
1900 - QS('p15agentConsoleText')['height'] = 'calc(100vh - ' + (81 + xh + xh2) + 'px)';
1901 - QS('p15agentConsoleText')['max-height'] = 'calc(100vh - ' + (81 + xh + xh2) + 'px)';
1902 - var xtermActive = !((args.xterm === 0) || ((terminal != null) && (xterm == null)));
1903 - if (fullscreen) {
1904 - QS('deskarea3x')['height'] = null;
1905 - QS('deskarea3x')['max-height'] = null;
1906 - QS('p14iframe')['height'] = null;
1907 - QS('p14iframe')['max-height'] = null;
1908 - if (xtermActive) {
1909 - QS('termarea3x')['height'] = 'calc(100vh - 55px)';
1910 - QS('termarea3xdiv')['height'] = 'calc(100vh - 55px)';
1911 - QS('termarea3x')['max-width'] = 'calc(1px)';
1912 - }
1913 - } else {
1914 - QS('deskarea3x')['height'] = 'calc(100vh - ' + (74 + xh + xh2) + 'px)';
1915 - QS('deskarea3x')['max-height'] = 'calc(100vh - ' + (74 + xh + xh2) + 'px)';
1916 - QS('p14iframe')['height'] = 'calc(100vh - ' + (23 + xh + xh2) + 'px)';
1917 - QS('p14iframe')['max-height'] = 'calc(100vh - ' + (23 + xh + xh2) + 'px)';
1918 - if (xtermActive) {
1919 - QS('termarea3x')['height'] = 'calc(100vh - ' + (74 + xh + xh2) + 'px)';
1920 - QS('termarea3xdiv')['height'] = 'calc(100vh - ' + (74 + xh + xh2) + 'px)';
1921 - QS('termarea3x')['max-width'] = 'calc(1px)';
1922 - }
1923 - }
1924 - QS('p43iframe')['height'] = 'calc(100vh - ' + (84 + xh) + 'px)';
1925 - QS('p43iframe')['max-height'] = 'calc(100vh - ' + (84 + xh) + 'px)';
1926 - QS('p6info')['height'] = 'calc(100vh - ' + (-50 + xh + xh2) + 'px)';
1927 - QS('p6info')['max-height'] = 'calc(100vh - ' + (-50 + xh + xh2) + 'px)';
1928 - QS('p10info')['height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
1929 - QS('p10info')['max-height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
1930 - QS('p17info')['height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
1931 - QS('p17info')['max-height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
1932 - QS('p20main')['height'] = 'calc(100vh - ' + (-50 + xh + xh2) + 'px)';
1933 - QS('p20main')['max-height'] = 'calc(100vh - ' + (-50 + xh + xh2) + 'px)';
1934 - QS('p21main')['height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
1935 - QS('p21main')['max-height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
1936 - QS('p30info')['height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
1937 - QS('p30info')['max-height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
1938 - QS('p51info')['height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
1939 - QS('p51info')['max-height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
1940 - QS('p16events')['height'] = 'calc(100vh - ' + (50 + xh + xh2) + 'px)';
1941 - QS('p16events')['max-height'] = 'calc(100vh - ' + (50 + xh + xh2) + 'px)';
1942 - QS('p31events')['height'] = 'calc(100vh - ' + (50 + xh + xh2) + 'px)';
1943 - QS('p31events')['max-height'] = 'calc(100vh - ' + (50 + xh + xh2) + 'px)';
1944 - QS('p41events')['height'] = 'calc(100vh - ' + (48 + xh + xh2) + 'px)';
1945 - QS('p41events')['max-height'] = 'calc(100vh - ' + (48 + xh + xh2) + 'px)';
1946 - QS('p52recordings')['height'] = 'calc(100vh - ' + (48 + xh + xh2) + 'px)';
1947 - QS('p52recordings')['max-height'] = 'calc(100vh - ' + (48 + xh + xh2) + 'px)';
1948 - QS('p60report')['height'] = 'calc(100vh - ' + (48 + xh + xh2) + 'px)';
1949 - QS('p60report')['max-height'] = 'calc(100vh - ' + (48 + xh + xh2) + 'px)';
1950 -
1951 - // We are looking at a single device, remove all the back buttons
1952 - if ((args.hide & 32) || ('{{currentNode}}'.toLowerCase() != '')) {
1953 - QV('p10BackButton', false);
1954 - QV('p11BackButton', false);
1955 - QV('p12BackButton', false);
1956 - QV('p13BackButton', false);
1957 - QV('p14BackButton', false);
1958 - QV('p15BackButton', false);
1959 - QV('p16BackButton', false);
1960 - QV('p17BackButton', false);
1961 - }
1962 - p1updateInfo();
1963 - }
1964 -
1965 - // Toggle the web page to full screen
1966 - function toggleAspectRatio(toggle) {
1967 - if (toggle === 1) { deskAspectRatio = ((deskAspectRatio + 1) % 3); putstore('deskAspectRatio', deskAspectRatio); }
1968 - deskAdjust();
1969 - }
1970 -
1971 - // If FullScreen, toggle menu to be horizontal or vertical
1972 - function toggleStackMenu(toggle) {
1973 - if (webPageFullScreen == true) {
1974 - if (toggle === 1) {
1975 - webPageStackMenu = !webPageStackMenu;
1976 - putstore('webPageStackMenu', webPageStackMenu);
1977 - }
1978 - if (webPageStackMenu == false) {
1979 - QC('body').remove('menu_stack');
1980 - } else {
1981 - QC('body').add('menu_stack');
1982 - if (xxcurrentView >= 10) QC('column_l').remove('room4submenu');
1983 - }
1984 - deskAdjust();
1985 - }
1986 - }
1987 -
1988 - // Toggle user interface menu
1989 - function showUserInterfaceSelectMenu() {
1990 - if (xxdialogMode) return;
1991 - Q('uiViewButton1').classList.remove('uiSelectorSel');
1992 - Q('uiViewButton2').classList.remove('uiSelectorSel');
1993 - Q('uiViewButton3').classList.remove('uiSelectorSel');
1994 - Q('uiViewButton4').classList.remove('uiSelectorSel');
1995 - Q('uiViewButton5').classList.remove('uiSelectorSel');
1996 - try { Q('uiViewButton' + uiMode).classList.add('uiSelectorSel'); } catch (ex) { }
1997 - QV('uiMenu', (QS('uiMenu').display == 'none'));
1998 - //Q('uiViewButton1').focus();
1999 - if (nightMode) { Q('uiViewButton4').classList.add('uiSelectorSel'); }
2000 - if (footerBar) { Q('uiViewButton5').classList.add('uiSelectorSel'); }
2001 - }
2002 -
2003 - function userInterfaceSelectMenu(s) {
2004 - if (s) { uiMode = s; putstore('uiMode', uiMode); }
2005 - webPageFullScreen = (uiMode < 3);
2006 - webPageStackMenu = (uiMode > 1);
2007 - toggleFullScreen(0);
2008 - toggleStackMenu(0);
2009 - if (webPageStackMenu && (xxcurrentView >= 10)) { QC('column_l').add('room4submenu'); } else { QC('column_l').remove('room4submenu'); }
2010 - adjustPanels();
2011 - }
2012 -
2013 - function toggleNightMode() {
2014 - if (xxdialogMode) return;
2015 - var cNightMode = getstore('nightMode', '0');
2016 - var x = '<input type=radio id=night0 name=nightmoderadio value=0 ' + ((cNightMode == 0)?'checked':'') + '><label for=night0>' + "Browser default" + '</label><br>';
2017 - x += '<input type=radio id=night2 name=nightmoderadio value=2 ' + ((cNightMode == 2)?'checked':'') + '><label for=night2>' + "Light mode" + '</label><br>';
2018 - x += '<input type=radio id=night1 name=nightmoderadio value=1 ' + ((cNightMode == 1)?'checked':'') + '><label for=night1>' + "Dark mode" + '</label><br>';
2019 - setDialogMode(2, "Night Mode", 3, toggleNightModeEx, x);
2020 - QV('uiMenu', false);
2021 - }
2022 -
2023 - function toggleNightModeEx() {
2024 - // Save new night mode
2025 - var nNightMode = '0';
2026 - if (Q('night1').checked) { nNightMode = '1'; }
2027 - if (Q('night2').checked) { nNightMode = '2'; }
2028 - putstore('nightMode', nNightMode);
2029 - setNightMode();
2030 - }
2031 -
2032 - function setNightMode() {
2033 - // Set night mode
2034 - var nNightMode = getstore('nightMode', '0')
2035 - nightMode = false;
2036 - if ((features2 & 0x00100000) != 0) { nNightMode = '1'; }
2037 - if ((features2 & 0x00200000) != 0) { nNightMode = '2'; }
2038 - if (nNightMode == '1') { nightMode = true; }
2039 - else if ((nNightMode == '0') && (window.matchMedia)) { nightMode = window.matchMedia('(prefers-color-scheme: dark)').matches }
2040 - if (nightMode) { QC('body').add('night'); QS('body')['background-color'] = '#000'; } else { QC('body').remove('night'); QS('body')['background-color'] = '#d3d9d6'; }
2041 - return nightMode;
2042 - }
2043 -
2044 - function toggleFooterBarMode() {
2045 - footerBar = !footerBar;
2046 - putstore('footerBar', footerBar?'1':'0');
2047 - QS('container')['grid-template-rows'] = null;
2048 - QS('container')['-ms-grid-rows'] = null;
2049 - adjustPanels();
2050 - }
2051 -
2052 - // Toggle the web page to full screen
2053 - function toggleFullScreen(toggle) {
2054 - if (toggle === 1) { webPageFullScreen = !webPageFullScreen; putstore('webPageFullScreen', webPageFullScreen); }
2055 - if (webPageFullScreen == false) {
2056 - QC('body').remove('menu_stack');
2057 - QC('body').remove('fullscreen');
2058 - QC('body').remove('arg_hide');
2059 - if (xxcurrentView >= 10) QC('column_l').add('room4submenu');
2060 - QV('UserDummyMenuSpan', false);
2061 - //QV('page_leftbar', false);
2062 - } else {
2063 - QC('body').add('fullscreen');
2064 - if (args.hide & 16) QC('body').add('arg_hide');
2065 - QV('page_leftbar', !(args.hide & 16));
2066 - QV('MainMenuSpan', !(args.hide & 16));
2067 - if (xxcurrentView >= 10) QC('column_l').remove('room4submenu');
2068 - QV('UserDummyMenuSpan', (xxcurrentView < 10) && webPageFullScreen);
2069 - }
2070 - mainUpdate(512);
2071 - QV('body', true);
2072 - }
2073 -
2074 - function getNodeFromId(id) { if (nodes != null) { for (var i in nodes) { if (nodes[i]._id == id) return nodes[i]; } } return null; }
2075 - function reload() {
2076 - var x = window.location.href;
2077 - if (x.endsWith('/#')) { x = x.substring(0, x.length - 2); }
2078 - window.location.href = x;
2079 - }
2080 -
2081 - function onStateChanged(server, state, prevState, errorCode) {
2082 - if (state == 0) {
2083 - // Control web socket disconnected
2084 - setDialogMode(0); // Close any dialog boxes if present
2085 - go(0); // Go to disconnection panel
2086 -
2087 - // Clean up
2088 - powerTimeline = null;
2089 - powerTimelineReq = null;
2090 - powerTimelineNode = null;
2091 - powerTimelineUpdate = null;
2092 - deviceShares = null;
2093 - deleteAllNotifications(); // Close and clear notifications if present
2094 - hideContextMenu(); // Hide the context menu if present
2095 - QV('verifyEmailId2', false);
2096 - QV('logoutControl', false);
2097 - if (errorCode == 'noauth') { QH('p0span', "Unable to perform authentication"); return; }
2098 - if (prevState == 2) { if (autoReconnect) { setTimeout(serverPoll, 5000); } } else { QH('p0span', "Unable to connect web socket"); }
2099 - if (authCookieRenewTimer != null) { clearInterval(authCookieRenewTimer); authCookieRenewTimer = null; }
2100 - } else if (state == 2) {
2101 - // Fetch list of meshes, nodes, files
2102 - meshserver.send({ action: 'usergroups' });
2103 - meshserver.send({ action: 'meshes' });
2104 - meshserver.send({ action: 'nodes', id: '{{currentNode}}', skip: (devicePagingState == null) ? 0 : devicePagingState.skip });
2105 - meshserver.send({ action: 'loginTokens' });
2106 - if (pluginHandler != null) { meshserver.send({ action: 'plugins' }); }
2107 - if ('{{currentNode}}'.toLowerCase() == '') { meshserver.send({ action: 'files' }); }
2108 - if ('{{viewmode}}'.toLowerCase() == '') { go(1); }
2109 - authCookieRenewTimer = setInterval(function () { meshserver.send({ action: 'authcookie' }); }, 1800000); // Request a cookie refresh every 30 minutes.
2110 - if (xxcurrentView == 40) { refreshServerTimelineStats(); }
2111 - }
2112 - }
2113 -
2114 - // Poll the server, if it responds, refresh the page.
2115 - function serverPoll() {
2116 - var xdr = null;
2117 - try { xdr = new XDomainRequest(); } catch (e) { }
2118 - if (!xdr) xdr = new XMLHttpRequest();
2119 - xdr.open('HEAD', window.location.href);
2120 - xdr.timeout = 15000;
2121 - // Make sure there isn't a reverse proxy in front that may just be returning 5xx codes
2122 - // Status code 4xx should still be allowed, since a page could potentially be removed, etc
2123 - xdr.onload = function () { if (xdr.status < 500) reload(); else setTimeout(serverPoll, 10000); };
2124 - xdr.onerror = xdr.ontimeout = function () { setTimeout(serverPoll, 10000); };
2125 - xdr.send();
2126 - }
2127 -
2128 - // Return true if this browser is Windows based
2129 - function detectWindowsBrowser() {
2130 - var userAgent = window.navigator.userAgent.toUpperCase();
2131 - return (userAgent.indexOf('WINDOWS') >= 0) || (userAgent.indexOf('WIN32') >= 0) || (userAgent.indexOf('WIN64') >= 0);
2132 - }
2133 -
2134 - function updateSiteAdmin() {
2135 - var serverFeatures = parseInt('{{{serverfeatures}}}');
2136 - var siteRights = userinfo.siteadmin;
2137 - var accountSettingsLocked = ((siteRights != 0xFFFFFFFF) && ((siteRights & 1024) != 0)) || ((features2 & 0x100) != 0); // Not admin and have account features locked, or using a loginToken
2138 -
2139 - // Update account actions
2140 - QV('p2AccountSecurity', ((features & 4) == 0) && (serverinfo.domainauth == false) && ((features & 4096) != 0) && (accountSettingsLocked == false)); // Hide Account Security if in single user mode or domain authentication, 2 factor auth not supported.
2141 - QV('p2AccountActions', !accountSettingsLocked)
2142 - QV('managePhoneNumber1', (features & 0x02000000) && (features & 0x04000000) && (serverinfo.lock2factor != true));
2143 - QV('managePhoneNumber2', (features & 0x02000000) && !(features & 0x04000000) && (serverinfo.lock2factor != true));
2144 - QV('manageMessaging1', (features2 & 0x02000000) && (features2 & 0x04000000) && (serverinfo.lock2factor != true));
2145 - QV('manageMessaging2', (features2 & 0x02000000) && !(features2 & 0x04000000) && (serverinfo.lock2factor != true));
2146 - QV('manageEmail2FA', (features & 0x00800000) && (serverinfo.lock2factor != true));
2147 - QV('p2AccountPassActions', ((features & 4) == 0) && (serverinfo.domainauth == false) && (userinfo != null) && (userinfo._id.split('/')[2].startsWith('~') == false)); // Hide Account Actions if in single user mode or domain authentication
2148 - //QV('p2AccountImage', ((features & 4) == 0) && (serverinfo.domainauth == false)); // If account actions are not visible, also remove the image on that panel
2149 - QV('accountCreateLoginTokenSpan', features2 & 0x00000080);
2150 - QV('p2AccountImageFrame', !accountSettingsLocked)
2151 - QV('p2ServerActions', (siteRights & 21) && ((serverFeatures & 15) != 0));
2152 - QV('LeftMenuMyServer', (siteRights & 21) && ((serverFeatures & 64) != 0)); // 16 + 4 + 1
2153 - QV('MainMenuMyServer', siteRights & 21);
2154 - QV('p2ServerActionsBackup', (siteRights & 1) && ((serverFeatures & 1) != 0));
2155 - QV('p2ServerActionsRestore', (siteRights & 4) && ((serverFeatures & 2) != 0));
2156 - QV('p2ServerActionsVersion', (siteRights & 16) && ((serverFeatures & 4) != 0));
2157 - QV('p2ServerActionsErrors', (siteRights & 16) && ((serverFeatures & 8) != 0));
2158 - QV('MainMenuMyFiles', siteRights & 8);
2159 - QV('LeftMenuMyFiles', siteRights & 8);
2160 - if (((siteRights & 8) == 0) && (xxcurrentView == 5)) { setDialogMode(0); go(1); }
2161 - if (currentNode != null) { gotoDevice(currentNode._id, xxcurrentView, true); }
2162 -
2163 - // Update user image
2164 - if ((userinfo.flags != null) && (userinfo.flags & 1)) {
2165 - if (userinfo.accountImageRnd == null) { userinfo.accountImageRnd = Math.floor(Math.random() * 9999999999); }
2166 - Q('p2AccountImage').src = 'userimage.ashx?rnd=' + userinfo.accountImageRnd;
2167 - } else {
2168 - Q('p2AccountImage').src = 'images/user-256.png';
2169 - }
2170 -
2171 - // Update user management state
2172 - if ((userinfo.siteadmin & 2) != 0) {
2173 - // We are user administrator
2174 - if (users == null) { meshserver.send({ action: 'users' }); }
2175 - if (wssessions == null) { meshserver.send({ action: 'wssessioncount' }); }
2176 - mainUpdate(8192 + 16384);
2177 - } else {
2178 - // We are not user administrator
2179 - users = null;
2180 - wssessions = null;
2181 - mainUpdate(16384);
2182 - if (xxcurrentView == 4 || ((xxcurrentView >= 30) && (xxcurrentView < 40))) { setDialogMode(0); go(1); currentUser = null; }
2183 - }
2184 - meshserver.send({ action: 'events', limit: parseInt(p3limitdropdown.value) });
2185 - QV('ServerConsole', (userinfo.siteadmin === 0xFFFFFFFF) && ((serverFeatures & 16) != 0));
2186 - QV('ServerTrace', (userinfo.siteadmin === 0xFFFFFFFF) && ((serverFeatures & 32) != 0));
2187 - if ((xxcurrentView == 115) && (userinfo.siteadmin != 0xFFFFFFFF)) { go(6); }
2188 - if ((xxcurrentView == 6) && ((userinfo.siteadmin & 21) == 0)) { go(1); }
2189 -
2190 - // If we are site administrator, register to get server statistics
2191 - if ((siteRights & 21) != 0) { meshserver.send({ action: 'serverstats', interval: 10000 }); }
2192 - }
2193 -
2194 - // To boost the speed of the web page when even floods occur, this method perform a delayed update on the web page.
2195 - var updateNaggleTimer = null;
2196 - var updateNaggleFlags = 0;
2197 - function mainUpdate(flags) {
2198 - updateNaggleFlags |= flags;
2199 - if (updateNaggleTimer == null) {
2200 - updateNaggleTimer = setTimeout(function () {
2201 - if (updateNaggleFlags & 512) { center(); }
2202 - if (updateNaggleFlags & 1) { onSearchInputChanged(); }
2203 - if (updateNaggleFlags & 2) { onSortSelectChange(false); }
2204 - if (updateNaggleFlags & 128) { updateMeshes(); }
2205 - if (updateNaggleFlags & 4) { updateDevices(); updateDeviceDetails(); if (xxcurrentView == 21) { p21updateMesh(); } }
2206 - if (updateNaggleFlags & 8) { drawNotifications(); }
2207 - if ((updateNaggleFlags & 16) && (features & 0x00008000)) { updateMapMarkers(); }
2208 - if (updateNaggleFlags & 32) { eventsUpdate(); }
2209 - if ((updateNaggleFlags & 64) && (features & 0x00008000)) { refreshMap(false, true); }
2210 - if (updateNaggleFlags & 256) { drawDeviceTimeline(); }
2211 - if (updateNaggleFlags & 1024) { deviceEventsUpdate(); }
2212 - if (updateNaggleFlags & 2048) { userEventsUpdate(); }
2213 - if (updateNaggleFlags & 4096) { p20updateMesh(); }
2214 - if (updateNaggleFlags & 8192) { updateUserGroups(); }
2215 - if (updateNaggleFlags & 16384) { updateUsers(); }
2216 - if (updateNaggleFlags & 32768) { updateRecordings(); }
2217 - if (updateNaggleFlags & 65536) { updateLoginTokens(); }
2218 - updateNaggleTimer = null;
2219 - updateNaggleFlags = 0;
2220 - gotoStartViewPage();
2221 - }, 150);
2222 - }
2223 - }
2224 -
2225 - // Return the number of 2nd factor for this account
2226 - function count2factoraAuths() {
2227 - if (userinfo == null) return -1;
2228 - var authFactorCount = 0;
2229 - if (userinfo.otpsecret == 1) { authFactorCount++; } // Authenticator time factor
2230 - if (userinfo.otphkeys > 0) { authFactorCount += userinfo.otphkeys; } // FIDO hardware factor
2231 - if ((features & 0x00800000) && (userinfo.otpekey == 1)) { authFactorCount++; } // EMail factor
2232 - if ((features & 0x02000000) && (features & 0x04000000) && (userinfo.phone != null)) { authFactorCount++; } // SMS factor
2233 - if ((features2 & 0x02000000) && (features2 & 0x04000000) && (userinfo.msghandle != null)) { authFactorCount++; } // Messaging factor
2234 - if ((authFactorCount > 0) && (userinfo.otpkeys > 0) && ((features2 & 0x40000) == 0)) { authFactorCount++; } // Backup keys
2235 - return authFactorCount;
2236 - }
2237 -
2238 - var backupCodesWarningDone = false;
2239 - function updateSelf() {
2240 - var authFactorCount = count2factoraAuths(); // Get the number of 2nd factors
2241 - var accountSettingsLocked = ((userinfo.siteadmin != 0xFFFFFFFF) && ((userinfo.siteadmin & 1024) != 0));
2242 - QV('verifyEmailId', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
2243 - QV('verifyEmailId2', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true) && (accountSettingsLocked == false));
2244 - QV('manageOtp', (serverinfo.lock2factor != true) && (authFactorCount > 0) && ((features2 & 0x40000) == 0));
2245 - QV('authPhoneNumberCheck', (userinfo.phone != null));
2246 - QV('authMessagingCheck', (userinfo.msghandle != null));
2247 - QV('authEmailSetupCheck', (userinfo.otpekey == 1) && (userinfo.email != null) && (userinfo.emailVerified == true));
2248 - QV('authAppSetupCheck', userinfo.otpsecret == 1);
2249 - QV('manageAuthApp', (serverinfo.lock2factor != true) && ((userinfo.otpsecret == 1) || ((features2 & 0x00020000) == 0)));
2250 - QV('authKeySetupCheck', userinfo.otphkeys > 0);
2251 - QV('authPushAuthDevCheck', (userinfo.otpdev > 0) && ((features2 & 0x40) != 0));
2252 - QV('authCodesSetupCheck', userinfo.otpkeys > 0);
2253 - QV('managePushAuthDev', (serverinfo.lock2factor != true) && (features2 & 0x40) && (authFactorCount > 0));
2254 - QV('manageHardwareOtp', (serverinfo.lock2factor != true));
2255 - mainUpdate(4 + 128 + 4096);
2256 -
2257 - // Check if none or at least 2 factors are enabled.
2258 - if ((backupCodesWarningDone == false) && (authFactorCount == 1) && ((features2 & 0x80000) == 0)) {
2259 - addNotification({ text: "Please add two-factor backup codes. If the current factor is lost, there is no way to recover this account.", title: "Two factor authentication", tag: 'backupcodes' });
2260 - backupCodesWarningDone = true;
2261 - }
2262 -
2263 - // If we can't create new groups, hide all links that can do that.
2264 - var newGroupsAllowed = ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 64) == 0));
2265 - QV('p2createMeshLink1', newGroupsAllowed);
2266 - QV('p2createMeshLink2', newGroupsAllowed);
2267 - QV('getStarted1', newGroupsAllowed);
2268 - QV('getStarted2', !newGroupsAllowed);
2269 -
2270 - if (typeof userinfo.passchange == 'number') {
2271 - if (userinfo.passchange == -1) { QH('p2nextPasswordUpdateTime', " - Reset on next login."); }
2272 - else if ((passRequirements != null) && (typeof passRequirements.reset == 'number')) {
2273 - var seconds = (userinfo.passchange) + (passRequirements.reset * 86400) - Math.floor(Date.now() / 1000);
2274 - if (seconds < 0) { QH('p2nextPasswordUpdateTime', " - Reset on next login."); }
2275 - else if (seconds < 3600) { var secs = Math.floor(seconds / 60); QH('p2nextPasswordUpdateTime',format((secs == 1)?" - Reset in 1 minute.":" - Reset in {0} minutes.", secs)); }
2276 - else if (seconds < 86400) { var hours = Math.floor(seconds / 3600); QH('p2nextPasswordUpdateTime', format((hours == 1) ? " - Reset in 1 hour." : " - Reset in {0} hours.", hours)); }
2277 - else { var days = Math.floor(seconds / 86400); QH('p2nextPasswordUpdateTime', format((hours == 1) ? " - Reset in 1 day." : " - Reset in {0} days.", days)); }
2278 - }
2279 - }
2280 -
2281 - // Adjust "My Users" tabs
2282 - QV('MainMenuMyUsers', ((users != null) && ((features & 4) == 0)) || (((userinfo.siteadmin & 512) != 0) && ((features & 0x08000000) != 0)));
2283 - QV('LeftMenuMyUsers', ((users != null) && ((features & 4) == 0)) || (((userinfo.siteadmin & 512) != 0) && ((features & 0x08000000) != 0)));
2284 - QV('UsersGeneral', ((users != null) && ((features & 4) == 0)));
2285 - QV('UsersGroups', ((users != null) && ((features & 4) == 0)));
2286 - QV('UsersRecordings', ((userinfo.siteadmin & 512) != 0) && ((features & 0x08000000) != 0));
2287 - }
2288 -
2289 - function setSessionActivity() { sessionActivity = Date.now(); QH('idleTimeoutNotify', ''); }
2290 - function checkIdleSessionTimeout() {
2291 - var delta = (Date.now() - sessionActivity);
2292 - if (delta > serverinfo.timeout) { window.location.href = 'logout'; } else {
2293 - var ds = Math.round((serverinfo.timeout - delta) / 1000);
2294 - if (ds <= 60) {
2295 - QH('idleTimeoutNotify', '<br />' + format((ds == 1)?"1 second until disconnect":"{0} seconds until disconnect", ds));
2296 - } else {
2297 - ds = Math.round(ds / 60);
2298 - if (ds <= 5) { QH('idleTimeoutNotify', '<br />' + format((ds == 1)?"1 minute until disconnect":"{0} minutes until disconnect", ds)); }
2299 - }
2300 - }
2301 - }
2302 -
2303 - function onMessage(server, message) {
2304 - switch (message.action) {
2305 - case 'trace': {
2306 - serverTrace.unshift(message);
2307 - displayServerTrace();
2308 - break;
2309 - }
2310 - case 'intersession' : {
2311 - if (message.subaction == 'removeNotify') { notificationDelete(message.id); }
2312 - break;
2313 - }
2314 - case 'traceinfo': {
2315 - if (typeof message.traceSources == 'object') {
2316 - if ((message.traceSources != null) && (message.traceSources.length > 0)) {
2317 - serverTraceSources = message.traceSources;
2318 - QH('p41traceStatus', EscapeHtml(message.traceSources.join(', ')));
2319 - } else {
2320 - serverTraceSources = [];
2321 - QH('p41traceStatus', "None");
2322 - }
2323 - }
2324 - break;
2325 - }
2326 - case 'serverstats': {
2327 - updateGeneralServerStats(message);
2328 - break;
2329 - }
2330 - case 'serverwarnings': {
2331 - if ((message.warnings != null) && (message.warnings.length > 0)) {
2332 - var ServerWarnings = {
2333 - 1: "MeshCentral SSH support requires NodeJS 11 or higher.",
2334 - 2: "Missing WebDAV parameters.",
2335 - 3: "Unrecognized configuration option \"{0}\".",
2336 - 4: "WebSocket compression is disabled, this feature is broken in NodeJS v11.11 to v12.15 and v13.2",
2337 - 5: "Unable to load Intel AMT TLS root certificate for default domain.",
2338 - 6: "Unable to load Intel AMT TLS root certificate for domain {0}.",
2339 - 7: "CIRA local FQDN's ignored when server in LAN-only or WAN-only mode.",
2340 - 8: "Can't have more than 4 CIRA local FQDN's. Ignoring value.",
2341 - 9: "Agent hash checking is being skipped, this is unsafe.",
2342 - 10: "Missing Let's Encrypt email address.",
2343 - 11: "Invalid Let's Encrypt host names.",
2344 - 12: "Invalid Let's Encrypt names, can't contain a *.",
2345 - 13: "Unable to setup Let's Encrypt module.",
2346 - 14: "Invalid Let's Encrypt names, unable to resolve: {0}",
2347 - 15: "Invalid Let's Encrypt email address, unable to resolve: {0}",
2348 - 16: "Unable to load CloudFlare trusted proxy IPv6 address list.",
2349 - 17: "SendGrid server has limited use in LAN mode.",
2350 - 18: "SMTP server has limited use in LAN mode.",
2351 - 19: "SMS gateway has limited use in LAN mode.",
2352 - 20: "Invalid \"LoginCookieEncryptionKey\" in config.json.",
2353 - 21: "Backup path can't be set within meshcentral-data folder, backup settings ignored.",
2354 - 22: "Failed to sign agent {0}: {1}",
2355 - 23: "Unable to load agent icon file: {0}.",
2356 - 24: "Unable to load agent logo file: {0}.",
2357 - 25: "This NodeJS version does not support OpenID.",
2358 - 26: "This NodeJS version does not support Discord.js."
2359 - };
2360 - var x = '';
2361 - for (var i in message.warnings) {
2362 - var y = message.warnings[i];
2363 - if (typeof y == 'string') {
2364 - x += '<div style=color:red;padding-bottom:6px><b>' + "WARNING: " + y + '</b></div>';
2365 - } else {
2366 - var z = ServerWarnings[y.id];
2367 - if (z == null) { z = y.msg; } else { if (y.args != null) { z = format(z, ...y.args); } }
2368 - x += '<div style=color:red;padding-bottom:6px><b>' + "WARNING: " + z + '</b></div>';
2369 - }
2370 - }
2371 - QH('serverWarnings', x);
2372 - QV('serverWarningsDiv', true);
2373 - }
2374 - break;
2375 - }
2376 - case 'servertimelinestats': {
2377 - setServerTimelineStats(message.events);
2378 - break;
2379 - }
2380 - case 'authcookie': {
2381 - // Got an authentication cookie refresh
2382 - authCookie = message.cookie;
2383 - authRelayCookie = message.rcookie;
2384 - break;
2385 - }
2386 - case 'serverinfo': {
2387 - serverinfo = message.serverinfo;
2388 - if (serverinfo.timeout) { setInterval(checkIdleSessionTimeout, 10000); checkIdleSessionTimeout(); }
2389 - if (debugmode == 1) { console.log('Server time: ', printDateTime(new Date(serverinfo.serverTime))); }
2390 - setupServiceWorker();
2391 - if (serverinfo.certExpire != null) {
2392 - var days = Math.floor((serverinfo.certExpire - Date.now()) / 86400000);
2393 - if ((days >= 0) && (days < 20)) {
2394 - QH('serverCertWarnings', '<div style=color:red;padding-bottom:6px><b>' + "WARNING: " + format("Certificate expires in {0} day(s)", days) + '</b></div>');
2395 - QV('serverWarningsDiv', true);
2396 - addNotification({ text: format("Certificate expires in {0} day(s)", days) });
2397 - }
2398 - }
2399 - if (serverinfo.preConfiguredRemoteInput) {
2400 - // Setup the pre-configured keyboard remote input strings
2401 - var x = '';
2402 - for (var i in serverinfo.preConfiguredRemoteInput) { x += '<div class="cmtext" onclick="cmdeskpreconfigtypeaction(' + i + ',event)">' + EscapeHtml(serverinfo.preConfiguredRemoteInput[i].name) + '</div>'; }
2403 - if (x != '') { x += '<hr />'; }
2404 - QH('deskPreConfigShortcutContextMenu1', x);
2405 - }
2406 - if (serverinfo.preConfiguredScripts) {
2407 - // Setup the pre-configured scripts
2408 - var x = '', y = '';
2409 - for (var i in serverinfo.preConfiguredScripts) {
2410 - var s = serverinfo.preConfiguredScripts[i];
2411 - var z = '<div class="cmtext" onclick="cmdeskpreconfigscriptaction(' + i + ',event)">' + EscapeHtml(serverinfo.preConfiguredScripts[i].name) + '</div>';
2412 - if (s.type != 3) { x += z; }
2413 - if (s.type >= 3) { y += z; }
2414 - }
2415 - QH('deskPreConfigScriptContextMenu1', x); // Windows devices
2416 - QH('deskPreConfigScriptContextMenu2', y); // Other devices
2417 - }
2418 - break;
2419 - }
2420 - case 'userinfo': {
2421 - userinfo = message.userinfo;
2422 - updateSiteAdmin();
2423 - updateSelf();
2424 - break;
2425 - }
2426 - case 'users': {
2427 - users = {};
2428 - for (var m in message.users) { users[message.users[m]._id] = message.users[m]; }
2429 - if (currentUser != null) { currentUser = users[currentUser._id]; }
2430 - mainUpdate(16384);
2431 - updateSelf();
2432 - break;
2433 - }
2434 - case 'wssessioncount': {
2435 - wssessions = message.wssessions;
2436 - mainUpdate(16384);
2437 - break;
2438 - }
2439 - case 'meshes': {
2440 - meshes = {};
2441 - for (var m in message.meshes) { meshes[message.meshes[m]._id] = message.meshes[m]; }
2442 - if (currentMesh != null) { currentMesh = meshes[currentMesh._id]; }
2443 - mainUpdate(4 + 128);
2444 - break;
2445 - }
2446 - case 'usergroups': {
2447 - var groupCount = 0;
2448 - if (Array.isArray(message.ugroups)) {
2449 - usergroups = {};
2450 - for (var i in message.ugroups) { groupCount++; usergroups[message.ugroups[i]._id] = message.ugroups[i]; }
2451 - if (groupCount == 0) { usergroups = null; }
2452 - } else {
2453 - usergroups = message.ugroups;
2454 - for (var i in message.ugroups) { groupCount++; message.ugroups[i]._id = i; }
2455 - if (groupCount == 0) { usergroups = null; }
2456 - }
2457 - mainUpdate(8192);
2458 - break;
2459 - }
2460 - case 'files': {
2461 - filetree = setupBackPointers(message.filetree);
2462 - updateFiles();
2463 - d3updatefiles();
2464 - break;
2465 - }
2466 - case 'nodes': {
2467 - nodes = [];
2468 - for (var m in message.nodes) {
2469 - for (var n in message.nodes[m]) {
2470 - if (message.nodes[m][n]._id == null) { console.log('Invalid node (' + n + '): ' + JSON.stringify(message.nodes)); continue; }
2471 - if (message.nodes[m][n].name == null) { message.nodes[m][n].name = '???'; }
2472 - message.nodes[m][n].namel = message.nodes[m][n].name.toLowerCase();
2473 - if (message.nodes[m][n].rname) { message.nodes[m][n].rnamel = message.nodes[m][n].rname.toLowerCase(); } else { message.nodes[m][n].rnamel = message.nodes[m][n].namel; }
2474 - message.nodes[m][n].meshnamel = meshes[m]?meshes[m].name.toLowerCase():'*';
2475 - message.nodes[m][n].meshid = m;
2476 - message.nodes[m][n].state = (message.nodes[m][n].state)?(message.nodes[m][n].state):0;
2477 - if (!message.nodes[m][n].icon) message.nodes[m][n].icon = 1;
2478 - message.nodes[m][n].ident = ++nodeShortIdent;
2479 - nodes.push(message.nodes[m][n]);
2480 - }
2481 - }
2482 -
2483 - // Remove any stars for nodes that don't exist
2484 - for (var i in stars) { if (getNodeFromId(i) == null) { delete stars[i]; } }
2485 -
2486 - // If we are currently looking at a node this is now gone, change the view.
2487 - if ((currentNode != null) && (IsNodeViewable(currentNode) == false)) { currentNode = null; go(1); }
2488 -
2489 - // Change the reference to the current node
2490 - if (currentNode != null) { currentNode = getNodeFromId(currentNode._id); if (currentNode != null) { gotoDevice(currentNode._id, xxcurrentView, true); } else { go(1); } }
2491 -
2492 - // Update device paging
2493 - devicePagingState = (message.totalcount == null) ? null : { total: message.totalcount, skip: message.skip, limit: message.limit };
2494 - updateDevicePageState();
2495 -
2496 - mainUpdate(1 | 2 | 4 | 64);
2497 - break;
2498 - }
2499 - case 'powertimeline': {
2500 - if (message.nodeid != powerTimelineReq) break;
2501 - powerTimelineNode = message.nodeid;
2502 - powerTimeline = message.timeline;
2503 - powerTimelineUpdate = Date.now() + 300000; // Update every 5 minutes
2504 - for (var i in powerTimeline) { if (i % 2 == 1) { powerTimeline[i] = powerTimeline[i] * 1000; } } // Decompress time
2505 - if (currentNode._id == message.nodeid) { mainUpdate(256); }
2506 - break;
2507 - }
2508 - case 'deviceShares': {
2509 - if (message.nodeid != deviceSharesReq) break;
2510 - deviceSharesNode = message.nodeid;
2511 - deviceShares = message.deviceShares;
2512 - if ((currentNode != null) && (currentNode._id == message.nodeid)) { gotoDevice(currentNode._id, xxcurrentView, true); }
2513 - break;
2514 - }
2515 - case 'deviceMeshShares': {
2516 - if (message.meshid != deviceSharesReq) break;
2517 - deviceSharesNode = message.meshid;
2518 - deviceShares = message.deviceShares;
2519 - if ((currentMesh != null) && (currentMesh._id == message.meshid)) { p20updateMesh(); }
2520 - break;
2521 - }
2522 - case 'getsysinfo': {
2523 - if (message.nodeid != powerTimelineReq) break;
2524 - if (message.noinfo === true) {
2525 - updateDeviceDetails(getNodeFromId(message.nodeid));
2526 - } else {
2527 - updateDeviceDetails(getNodeFromId(message.nodeid), message.hardware);
2528 - }
2529 - break;
2530 - }
2531 - case 'lastconnect': {
2532 - var node = getNodeFromId(message.nodeid);
2533 - if (node != null) {
2534 - node.lastconnect = message.time;
2535 - node.lastaddr = message.addr;
2536 - if ((currentNode._id == node._id) && (Q('MainComputerState').innerHTML == '') && (node.lastconnect != null)) {
2537 - QH('MainComputerState', '<span>' + "Last seen:" + '<br />' + printDateTime(new Date(node.lastconnect)) + '</span>');
2538 - }
2539 - }
2540 - break;
2541 - }
2542 - case 'lastconnects': {
2543 - var lcnodes = Object.keys(message.lastconnects);
2544 - for (var i in lcnodes) {
2545 - var lcnodeid = lcnodes[i];
2546 - var node = getNodeFromId(lcnodeid);
2547 - if (node != null) { node.lastconnect = message.lastconnects[lcnodeid] }
2548 - }
2549 - mainUpdate(4);
2550 - }
2551 - case 'msg': {
2552 - // Check if this is a message from a node
2553 - if (message.nodeid != null) {
2554 - var index = -1;
2555 - if (nodes != null) { for (var i in nodes) { if (nodes[i]._id == message.nodeid) { index = i; break; } } }
2556 - if (index != -1) {
2557 - // Node was found, dispatch the message
2558 - if ((message.type === 'cpuinfo') && (currentNode != null) && (currentNode._id == message.nodeid)) {
2559 - var now = (Date.now() / 1000), cpu = 0, memory = 0;
2560 - if (typeof message.cpu.total == 'number') { cpu = message.cpu.total; }
2561 - if (typeof message.memory.percentConsumed == 'number') { memory = message.memory.percentConsumed; }
2562 - deviceDetailsStatsData.push([now, cpu, memory]);
2563 - deviceDetailsStatsDraw(message);
2564 - } else if (message.type === 'console') { p15consoleReceive(nodes[index], message.value, message.source); } // This is a console message.
2565 - else if (message.type === 'notify') { // This is a notification message.
2566 - var n = getstore('notifications', 0);
2567 - if (((n & 8) == 0) && (message.amtMessage != null)) { break; } // Intel AMT desktop & terminal messages should be ignored.
2568 - var n = { text: message.value, title: message.title, icon: message.icon, titleid: message.titleid, msgid: message.msgid, args: message.args };
2569 - if (message.id != null) { n.id = message.id; }
2570 - if (message.nodeid != null) { n.nodeid = message.nodeid; }
2571 - if (message.tag != null) { n.tag = message.tag; }
2572 - if (message.url != null) { n.url = message.url; }
2573 - if (message.username != null) { n.username = message.username; }
2574 - if (typeof message.maxtime === 'number') { n.maxtime = message.maxtime; }
2575 - addNotification(n);
2576 - } else if (message.type === 'ps') {
2577 - showDeskToolsProcesses(message);
2578 - } else if (message.type === 'services') {
2579 - showDeskToolsServices(message);
2580 - } else if ((message.type === 'getclip') && (currentNode != null) && (currentNode._id == message.nodeid)) {
2581 - if ((message.tag == 1) && (xxdialogTag === 'clipboard')) {
2582 - Q('d2clipText').value = message.data; // Put remote clipboard data into dialog box
2583 - } else if (message.tag === 2) {
2584 - if (navigator.clipboard != null) { navigator.clipboard.writeText(message.data).then(function() { }).catch(function(err) { console.log(err); }) } // Put remote clipboard data into our clipboard
2585 - }
2586 - } else if ((message.type === 'setclip') && (xxdialogTag === 'clipboard') && (currentNode != null) && (currentNode._id == message.nodeid)) {
2587 - // Display success/fail on the clipboard dialog box.
2588 - QH('dlgClipStatus', message.success ? '<span style=color:green>' + "Success" + '</span>' : '<span style=color:red>' + "Failed" + '</span>')
2589 - setTimeout(function () { try { QH('dlgClipStatus', ''); } catch (ex) { } }, 2000);
2590 - } else if ((message.type === 'userSessions') && (currentNode != null) && (currentNode._id === message.nodeid) && (desktop == null)) {
2591 - // Got list of user sessions
2592 - var userSessions = [];
2593 - if (message.data != null) { for (var i in message.data) { if ((message.data[i].State == 'Active') || (message.data[i].State == 'Connected') || (message.data[i].StationName == 'Console') || (debugmode == 3)) { userSessions.push(message.data[i]); } } }
2594 - if (userSessions.length === 0) { connectDesktop(null, 1, null, message.tag); } // No active sessions, do a normal connection.
2595 - else if (userSessions.length === 1) { connectDesktop(null, 1, userSessions[0].SessionId, message.tag); } // One active session, connect to it
2596 - else {
2597 - var x = '';
2598 - for (var i in userSessions) {
2599 - x += '<div style="text-align:left;cursor:pointer;background-color:gray;margin:5px;padding:5px;border-radius:5px" onclick=connectDesktop(event,1,' + userSessions[i].SessionId + ',' + message.tag + ')>' + userSessions[i].State + (userSessions[i].StationName ? (', ' + userSessions[i].StationName) : '');
2600 - if (userSessions[i].Username) { if (userSessions[i].Domain) { x += ' - ' + userSessions[i].Domain + '/' + userSessions[i].Username; } else { x += ' - ' + userSessions[i].Username; } }
2601 - x += '</div>';
2602 - }
2603 - QH('p11DeskSessionSelector', x);
2604 - QV('p11DeskSessionSelector', true);
2605 - }
2606 - } else if (message.type === 'psinfo') {
2607 - if (xxdialogTag === ('ps|' + message.nodeid + '|' + message.pid)) {
2608 - var x = '<div style=max-height:200px;overflow-y:auto>';
2609 - //x += addHtmlValue4("Process ID", message.pid);
2610 - if ((typeof message.value == 'object') && (Object.keys(message.value).length > 0)) {
2611 - if (message.value.processName) { x += '<div style=padding:4px><div style=padding-bottom:4px>' + "Process Name" + '</div><div style=word-wrap:break-word;padding-left:6px><b>' + message.value.processName + '</b></div></div>'; }
2612 - if (message.value.machineName) { x += addHtmlValue5("Machine Name", message.value.machineName); }
2613 - if (message.value.cmd) { x += '<div style=padding:4px><div style=padding-bottom:4px>' + "Command Line" + '</div><div style=word-wrap:break-word;padding-left:6px><b>' + message.value.cmd + '</b></div></div>'; }
2614 - if (message.value.mainWindowTitle) { x += '<div style=padding:4px><div style=padding-bottom:4px>' + "Window Title" + '</div><div style=word-wrap:break-word;padding-left:6px><b>' + message.value.mainWindowTitle + '</b></div></div>'; }
2615 - if (message.value.processUser) { x += addHtmlValue5("User", message.value.processUser); }
2616 - if (message.value.processDomain) { x += addHtmlValue5("Domain", message.value.processDomain); }
2617 - if (message.value.startTime) { x += addHtmlValue5("Start Time", message.value.startTime); }
2618 - x += '<hr />';
2619 - if (message.value.PriorityBoostEnabled) { x += addHtmlValue5("Priority Boost", message.value.PriorityBoostEnabled?"Enabled":"Disabled"); }
2620 - if (message.value.sessionId) { x += addHtmlValue5("Session Id", message.value.sessionId); }
2621 - if (message.value.handleCount) { x += addHtmlValue5("Handle Count", message.value.handleCount); }
2622 - if (message.value.privilegedProcessorTime) { x += addHtmlValue5("Privileged Processor Time", format("{0} seconds", message.value.privilegedProcessorTime)); }
2623 - if (message.value.totalProcessorTime) { x += addHtmlValue5("Total Processor Time", format("{0} seconds", message.value.totalProcessorTime)); }
2624 - if (message.value.userProcessorTime) { x += addHtmlValue5("User Processor Time", format("{0} seconds", message.value.userProcessorTime)); }
2625 - if (message.value.nonpagedSystemMemorySize) { x += addHtmlValue5("Non Paged Memory", getNiceSize2(message.value.nonpagedSystemMemorySize)); }
2626 - if (message.value.pagedMemorySize) { x += addHtmlValue5("Paged Memory", getNiceSize2(message.value.pagedMemorySize)); }
2627 - if (message.value.privateMemorySize) { x += addHtmlValue5("Private Memory", getNiceSize2(message.value.privateMemorySize)); }
2628 - if (message.value.virtualMemorySize) { x += addHtmlValue5("Virtual Memory", getNiceSize2(message.value.virtualMemorySize)); }
2629 - if (message.value.workingSet) { x += addHtmlValue5("Working Set", getNiceSize2(message.value.workingSet)); }
2630 - if (message.value.peakWorkingSet) { x += addHtmlValue5("Peak Working Set", getNiceSize2(message.value.peakWorkingSet)); }
2631 - if (message.value.peakPagedMemorySize) { x += addHtmlValue5("Peak Paged Memory", getNiceSize2(message.value.peakPagedMemorySize)); }
2632 - if (message.value.peakVirtualMemorySize) { x += addHtmlValue5("Peak Virtual Memory", getNiceSize2(message.value.peakVirtualMemorySize)); }
2633 - } else {
2634 - x += "No information provided";
2635 - }
2636 - x += '</div>';
2637 - QH('id_dialogOptions', x);
2638 - }
2639 - }
2640 - }
2641 - } else {
2642 - if (message.type === 'notify') { // This is a notification message.
2643 - var n = { text: message.value, title: message.title, icon: message.icon, titleid: message.titleid, msgid: message.msgid, args: message.args };
2644 - if (message.id != null) { n.id = message.id; }
2645 - if (message.tag != null) { n.tag = message.tag; }
2646 - if (message.url != null) { n.url = message.url; }
2647 - if (message.username != null) { n.username = message.username; }
2648 - if (typeof message.maxtime == 'number') { n.maxtime = message.maxtime; }
2649 - addNotification(n);
2650 - }
2651 - }
2652 - break;
2653 - }
2654 - case 'getnetworkinfo': {
2655 - if (currentNode._id != message.nodeid) return;
2656 - updateDeviceDetails(getNodeFromId(message.nodeid), null, message);
2657 - if ((xxdialogMode == 2) && (xxdialogTag == 'if' + message.nodeid)) {
2658 - var x = '<div class=dialogText>';
2659 - if (currentNode.lastconnect) { x += addHtmlValue2("Last agent connection", printDateTime(new Date(currentNode.lastconnect))); }
2660 - if (currentNode.lastaddr) {
2661 - var splitip = currentNode.lastaddr.split(':');
2662 - if (splitip.length > 2) {
2663 - // IPv6
2664 - x += addHtmlValue2("Last agent address", currentNode.lastaddr + ' <img src="images/link4.png" title="' + "Copy address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(currentNode.lastaddr) + '") width=10 height=10>');
2665 - } else {
2666 - // IPv4
2667 - if (isPrivateIP(currentNode.lastaddr)) {
2668 - x += addHtmlValue2("Last agent address", splitip[0] + ' <img src="images/link4.png" title="' + "Copy address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(splitip[0]) + '") width=10 height=10>');
2669 - } else {
2670 - x += addHtmlValue2("Last agent address", '<a href="https://iplocation.com/?ip=' + splitip[0] + '" rel="noreferrer noopener" target="MeshIPLoopup">' + splitip[0] + '</a> <img src="images/link4.png" title="' + "Copy address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(splitip[0]) + '") width=10 height=10>');
2671 - }
2672 - }
2673 - }
2674 - if (message.updateTime != null) { x += addHtmlValue2("Last interfaces update", printDateTime(new Date(message.updateTime))); }
2675 -
2676 - if (message.netif != null) {
2677 - // Old style
2678 - for (var i in message.netif) {
2679 - var net = message.netif[i];
2680 - x += '<hr />'
2681 - if (net.name) { x += addHtmlValue2("Name", '<b>' + EscapeHtml(net.name) + '</b>'); }
2682 - if (net.desc) { x += addHtmlValue2("Description", EscapeHtml(net.desc).replace('(R)', '&reg;').replace('(r)', '&reg;')); }
2683 - if (net.dnssuffix) { x += addHtmlValue2("DNS suffix", EscapeHtml(net.dnssuffix) + ' <img src="images/link4.png" title="' + "Copy name to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(net.dnssuffix) + '") width=10 height=10>'); }
2684 - if (net.mac) { x += addHtmlValue2("MAC address", '<a href="https://dnslytics.com/mac-address-lookup/' + net.mac.substring(0, 6) + '" rel="noreferrer noopener" target="MeshMACLoopup">' + EscapeHtml(net.mac.toLowerCase()) + '</a> <img src="images/link4.png" title="' + "Copy MAC address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(net.mac.toLowerCase()) + '") width=10 height=10>'); }
2685 - if (net.v4addr) { x += addHtmlValue2("IPv4 address", EscapeHtml(net.v4addr) + ' <img src="images/link4.png" title="' + "Copy address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(net.v4addr) + '") width=10 height=10>'); }
2686 - if (net.v4mask) { x += addHtmlValue2("IPv4 mask", EscapeHtml(net.v4mask) + ' <img src="images/link4.png" title="' + "Copy address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(net.v4mask) + '") width=10 height=10>'); }
2687 - if (net.v4gateway) { x += addHtmlValue2("IPv4 gateway", EscapeHtml(net.v4gateway) + ' <img src="images/link4.png" title="' + "Copy address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(net.v4gateway) + '") width=10 height=10>'); }
2688 - if (net.gatewaymac) { x += addHtmlValue2("Gateway MAC", '<a href="https://dnslytics.com/mac-address-lookup/' + net.gatewaymac.substring(0, 6) + '" rel="noreferrer noopener" target="MeshMACLoopup">' + EscapeHtml(net.gatewaymac.toLowerCase()) + '</a> <img src="images/link4.png" title="' + "Copy MAC address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(net.gatewaymac.toLowerCase()) + '") width=10 height=10>'); }
2689 - }
2690 - } else if (message.netif2 != null) {
2691 - // New style
2692 - for (var i in message.netif2) {
2693 - var net = message.netif2[i];
2694 - if ((Array.isArray(net) == false) || (net.length < 1) || (net[0] == null) || ((typeof net[0].mac == 'string') && (net[0].mac.startsWith('00:00:00:00')))) continue;
2695 - x += '<hr />'
2696 - x += addHtmlValue2("Name", '<b>' + EscapeHtml(i) + '</b>');
2697 - if (typeof net[0].mac == 'string') { x += addHtmlValue2("MAC address", '<a href="https://dnslytics.com/mac-address-lookup/' + net[0].mac.split(':').join('').substring(0, 6) + '" rel="noreferrer noopener" target="MeshMACLoopup">' + EscapeHtml(net[0].mac.toLowerCase()) + '</a> <img src="images/link4.png" title="' + "Copy MAC address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(net[0].mac.toLowerCase()) + '") width=10 height=10>'); }
2698 - if (net[0].fqdn) { x += addHtmlValue2("FQDN", net[0].fqdn); }
2699 - for (var j = 0; j < net.length; j++) {
2700 - var netif = net[j];
2701 - if (netif.family == 'IPv6') {
2702 - if (netif.address) { x += addHtmlValue2("IPv6 address", EscapeHtml(netif.address) + ' <img src="images/link4.png" title="' + "Copy address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(netif.address) + '") width=10 height=10>'); }
2703 - if (netif.netmask) { x += addHtmlValue2("IPv6 mask", EscapeHtml(netif.netmask) + ' <img src="images/link4.png" title="' + "Copy address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(netif.netmask) + '") width=10 height=10>'); }
2704 - if (netif.gateway) { x += addHtmlValue2("IPv6 gateway", EscapeHtml(netif.gateway) + ' <img src="images/link4.png" title="' + "Copy address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(netif.gateway) + '") width=10 height=10>'); }
2705 - } else if (netif.family == 'IPv4') {
2706 - if (netif.address) { x += addHtmlValue2("IPv4 address", EscapeHtml(netif.address) + ' <img src="images/link4.png" title="' + "Copy address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(netif.address) + '") width=10 height=10>'); }
2707 - if (netif.netmask) { x += addHtmlValue2("IPv4 mask", EscapeHtml(netif.netmask) + ' <img src="images/link4.png" title="' + "Copy address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(netif.netmask) + '") width=10 height=10>'); }
2708 - if (netif.gateway) { x += addHtmlValue2("IPv4 gateway", EscapeHtml(netif.gateway) + ' <img src="images/link4.png" title="' + "Copy address to clipboard" + '" style="cursor:pointer" onclick=copyTextToClip2("' + encodeURIComponentEx(netif.gateway) + '") width=10 height=10>'); }
2709 - }
2710 - }
2711 - }
2712 - } else {
2713 - x += '<hr />'
2714 - x += "No network interface information available for this device.";
2715 - }
2716 - x += '</div>';
2717 - QH('d2netinfo', x);
2718 - }
2719 - break;
2720 - }
2721 - case 'serverversion': {
2722 - if ((xxdialogMode == 2) && (xxdialogTag == 'MeshCentralServerUpdate')) {
2723 - var x = '<div class=dialogText>';
2724 - if (!message.tags.current) { message.tags.current = "Unknown"; }
2725 - if (!message.tags.stable) { message.tags.stable = "Unknown"; }
2726 - if (!message.tags.latest) { message.tags.latest = "Unknown"; }
2727 - x += addHtmlValue2("Current Version", '<b>' + EscapeHtml(message.tags.current) + '</b>');
2728 - x += '<hr />';
2729 - x += addHtmlValue2('<label><input id=d2updateCheck1 type=checkbox onclick=server_showVersionDlgUpdate()' + (((message.tags.stable == "Unknown") || (message.tags.current == message.tags.stable) || ((features & 2048) == 0))?' disabled':'') + ' /> ' + "Stable Version" + '</label>', '<b>' + EscapeHtml(message.tags.stable) + '</b>');
2730 - x += addHtmlValue2('<label><input id=d2updateCheck2 type=checkbox onclick=server_showVersionDlgUpdate()' + (((message.tags.latest == "Unknown") || (message.tags.current == message.tags.latest) || ((features & 2048) == 0))?' disabled':'') + ' /> ' + "Latest Version" + '</label>', '<b>' + EscapeHtml(message.tags.latest) + '</b>');
2731 - x += '</div>';
2732 - if (((message.tags.current == message.tags.latest) && (message.tags.current == message.tags.stable)) || ((features & 2048) == 0)) {
2733 - setDialogMode(2, "MeshCentral Version", 1, null, x);
2734 - } else {
2735 - x += '<hr />' + "Check and click OK to start server self-update.";
2736 - setDialogMode(2, "MeshCentral Version", 3, server_showVersionDlgEx, x, message.tags);
2737 - server_showVersionDlgUpdate();
2738 - }
2739 - }
2740 - break;
2741 - }
2742 - case 'servererrors': {
2743 - if ((xxdialogMode == 2) && (xxdialogTag == 'MeshCentralServerErrors')) {
2744 - if (message.data == null) {
2745 - setDialogMode(2, "MeshCentral Server Errors", 1, null, "Server has no error log.");
2746 - } else {
2747 - var x = '<div class="dialogText dialogTextLog"><pre id=d2ServerErrorsLogPre>' + EscapeHtml(message.data) + '</pre></div>';
2748 - x += '<br /><div style=float:right><img src=images/link4.png height=10 width=10 title="' + "Download error log" + '" style=cursor:pointer onclick=d2CopyServerErrorsToClip()></div>';
2749 - x += '<div><label><input id=d2clearErrorsCheck type=checkbox onclick=server_showErrorsDlgUpdate() /> ' + "Check and click OK to clear error log." + '</label></div>';
2750 - setDialogMode(2, "MeshCentral Server Errors", 3, server_showErrorsDlgEx, x);
2751 - server_showErrorsDlgUpdate();
2752 - }
2753 - }
2754 - break;
2755 - }
2756 - case 'serverconsole': {
2757 - p15consoleReceive('serverconsole', message.value);
2758 - break;
2759 - }
2760 - case 'events': {
2761 - if ((message.nodeid != null) && (currentNode != null) && (message.nodeid == currentNode._id)) {
2762 - currentDeviceEvents = message.events;
2763 - mainUpdate(1024);
2764 - } else if ((message.userid != null) && (currentUser != null) && (message.userid == currentUser._id)) {
2765 - currentUserEvents = message.events;
2766 - mainUpdate(2048);
2767 - } else {
2768 - events = message.events;
2769 - mainUpdate(32);
2770 - }
2771 - break;
2772 - }
2773 - case 'recordings': {
2774 - p52recordings = message.events;
2775 - if (message.error != null) { p52recordings = message.error; }
2776 - updateRecordings();
2777 - break;
2778 - }
2779 - case 'getcookie': {
2780 - if (message.tag == 'MCRouter') {
2781 - var servername = serverinfo.name;
2782 - if ((servername.indexOf('.') == -1) || ((features & 2) != 0)) { servername = window.location.hostname; } // If the server name is not set or it's in LAN-only mode, use the URL hostname as server name.
2783 - var domainUrlNoSlash = domainUrl.substring(0, domainUrl.length - 1);
2784 - var portStr = (serverinfo.port == 443) ? '' : (':' + serverinfo.port);
2785 - var url = 'mcrouter://' + servername + portStr + domainUrl + 'control.ashx?c=' + authCookie + '&t=' + serverinfo.tlshash + '&l={{{lang}}}' + (urlargs.key?('&key=' + urlargs.key):'');
2786 - if (message.nodeid != null) { url += ('&nodeid=' + message.nodeid); }
2787 - if (message.tcpport != null) { url += ('&protocol=1&remoteport=' + message.tcpport); }
2788 - if (message.localRelay) { url += '&local=1'; }
2789 - if (message.localport) { url += '&localport=' + message.localport; }
2790 - if (message.name != null) { url += ('&name=' + encodeURIComponentEx(message.name)); }
2791 - if (message.ip != null) { url += ('&remoteip=' + message.ip); }
2792 - url += ('&appid=' + message.protocol + '&autoexit=1'); // Protocol: 0 = Custom, 1 = HTTP, 2 = HTTPS, 3 = RDP, 4 = PuTTY, 5 = WinSCP, 6 = MCRDesktop, 7 = MCRFiles
2793 - //console.log(url);
2794 - downloadFile(url, '');
2795 - } else if (message.tag == 'novnc') {
2796 - var vncurl = window.location.origin + domainUrl + 'novnc/vnc.html?ws=wss%3A%2F%2F' + window.location.host + encodeURIComponentEx(domainUrl) + (message.localRelay?'local':'mesh') + 'relay.ashx%3Fauth%3D' + message.cookie + '&show_dot=1' + (urlargs.key?('&key=' + urlargs.key):'') + '&l={{{lang}}}';
2797 - var node = getNodeFromId(message.nodeid);
2798 - if (node != null) { vncurl += '&name=' + encodeURIComponentEx(node.name); }
2799 - safeNewWindow(vncurl, 'mcnovnc/' + message.nodeid);
2800 - } else if (message.tag == 'mstsc') {
2801 - var rdpurl = window.location.origin + domainUrl + 'mstsc.html?ws=' + message.cookie + (urlargs.key?('&key=' + urlargs.key):'');
2802 - var node = getNodeFromId(message.nodeid);
2803 - if (node != null) { rdpurl += '&name=' + encodeURIComponentEx(node.name); }
2804 - if (message.localRelay) { rdpurl += '&local=1'; }
2805 - safeNewWindow(rdpurl, 'mcmstsc/' + message.nodeid);
2806 - } else if (message.tag == 'ssh') {
2807 - var sshurl = window.location.origin + domainUrl + 'ssh.html?ws=' + message.cookie + (urlargs.key?('&key=' + urlargs.key):'');
2808 - var node = getNodeFromId(message.nodeid);
2809 - if (node != null) { sshurl += '&name=' + encodeURIComponentEx(node.name); }
2810 - if (message.localRelay) { sshurl += '&local=1'; }
2811 - safeNewWindow(sshurl, 'mcssh/' + message.nodeid);
2812 - }
2813 - break;
2814 - }
2815 - case 'getNotes': {
2816 - var n = Q('d2devNotes');
2817 - if (n && (message.id == decodeURIComponent(n.attributes['noteid'].value))) {
2818 - if (message.notes) { QH('d2devNotes', decodeURIComponent(message.notes)); } else { QH('d2devNotes', ''); }
2819 - var ro = (n.attributes['ro'].value == 'true');
2820 - if (ro == false) { // If we have permissions, set read/write on this note.
2821 - n.removeAttribute('readonly');
2822 - QE('idx_dlgOkButton', true);
2823 - QV('idx_dlgOkButton', true);
2824 - focusTextBox('d2devNotes');
2825 - }
2826 - }
2827 - break;
2828 - }
2829 - case 'otpauth-request': {
2830 - if ((xxdialogMode == 2) && (xxdialogTag == 'otpauth-request')) {
2831 - if (message.err != null) {
2832 - var otpauthErrors = [ '', "2FA is locked", "Backup codes are locked", "Login token in use", "OTP 2FA not allowed", "Account is locked", "Unable to load OTPLIB" ];
2833 - if ((message.err > 0) && (message.err < otpauthErrors.length)) { QH('d2optinfo', otpauthErrors[message.err]); } else { QH('d2optinfo', format("Error #{0}", message.err)); }
2834 - } else {
2835 - var secret = message.secret;
2836 - if (secret.length == 52) { secret = secret.split(/(.............)/).filter(Boolean).join(' '); }
2837 - else if (secret.length == 32) { secret = secret.split(/(....)/).filter(Boolean).join(' '); secret = secret.substring(0, 20) + '<br/>' + secret.substring(20) }
2838 - QH('d2optinfo', '<table style=width:380px><tr><td style=vertical-align:top>' + format("Install <a href=\"https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2\" rel=\"noreferrer noopener\" target=_blank>Google Authenticator</a> or a compatible application and scan the barcode, use <a href=\"{0}\" rel=\"noreferrer noopener\" target=_blank>this link</a> or enter the secret. Then, enter the current 6 digit token below to activate 2-Step login.", message.url) + '<br /><br />' + "Secret" + '<br /><tt id=d2optsecret secret="' + message.secret + '" style=font-size:12px>' + secret + '</tt><br /><br /></td><td style=width:1px;vertical-align:top><a href="' + message.url + '" rel="noreferrer noopener" target=_blank><div id="qrcode"></div></a></td><tr><td colspan=2 style="text-align:center;border-top:1px solid black"><br />' + "Enter the token here for 2-step login:" + ' <input type=text autocomplete="one-time-code" inputmode="numeric" pattern="[0-9]*" onkeypress="return (event.keyCode == 8) || (event.charCode >= 48 && event.charCode <= 57)" onkeyup=account_addOtpCheck(event) onkeydown=account_addOtpCheck() maxlength=6 id=d2otpauthinput type=text></td></table>');
2839 - new QRCode(Q('qrcode'), { text: message.url, width: 128, height: 128, colorDark: '#000000', colorLight: '#EEE', correctLevel: QRCode.CorrectLevel.H });
2840 - QV('idx_dlgOkButton', true);
2841 - QE('idx_dlgOkButton', false);
2842 - Q('d2otpauthinput').focus();
2843 - }
2844 - }
2845 - break;
2846 - }
2847 - case 'otpauth-setup': {
2848 - if (xxdialogMode) return;
2849 - setDialogMode(2, "Authenticator App", 1, null, message.success ? ('<b style=color:green>' + "Authenticator app activation successful." + '</b> ' + "You will now need a valid token to login again.") : ('<b style=color:red>' + "2-step login activation failed." + '</b> ' + "Clear the secret from the application and try again. You only have a few minutes to enter the proper code."));
2850 - break;
2851 - }
2852 - case 'otpauth-clear': {
2853 - if (xxdialogMode) return;
2854 - setDialogMode(2, "Authenticator App", 1, null, message.success ? ('<b>' + "Authenticator application removed." + '</b> ' + "You can reactivate this feature at any time.") : ('<b style=color:red>' + "2-step login activation removal failed." + '</b> ' + "Try again."));
2855 - break;
2856 - }
2857 - case 'otpauth-getpasswords': {
2858 - if (xxdialogMode) return;
2859 - var x = "One time tokens can be used as secondary authentication. Generate a set, print them and keep them in a safe place.";
2860 - x += '<div style="border-radius:6px;border: 2px dashed #888;width:100%;margin-top:8px"><div style="padding:8px;font-family:Arial, Helvetica, sans-serif;font-size:20px;font-weight:bold"><table class=selecttext style=width:100%;text-align:center>';
2861 - if (message.passwords) {
2862 - var j = 0, clipb = '';
2863 - for (var i in message.passwords) {
2864 - if (++j % 2) { x += '<tr>'; }
2865 - var p = '' + message.passwords[i].p;
2866 - while (p.length < 8) { p = '0' + p; }
2867 - if (message.passwords[i].u === true) {
2868 - x += '<td>' + p.substring(0, 4) + '&nbsp;' + p.substring(4);
2869 - if (clipb != '') { clipb += ' '; }
2870 - clipb += p;
2871 - } else {
2872 - x += '<td><strike style=color:#BBB>' + p.substring(0, 4) + '&nbsp;' + p.substring(4); + '</strike>';
2873 - }
2874 - }
2875 - } else {
2876 - x += '<tr><td>' + "No Active Tokens";
2877 - }
2878 - x += '</table></div></div><br />';
2879 - x += '<div><input type=button value=' + "Close" + ' onclick=setDialogMode(0) style=float:right></input>';
2880 - x += '<input type=button value="' + "Generate New Tokens" + '" onclick="account_manageOtp(1);"></input>';
2881 - if (message.passwords != null) {
2882 - x += '<input type=button value="' + "Clear Tokens" + '" onclick="account_manageOtp(2);"></input>';
2883 - x += '&nbsp;<img src=images/link4.png height=10 width=10 title="' + "Copy valid codes to clipboard" + '" style=cursor:pointer onclick=copyTextToClip2("' + encodeURIComponentEx(clipb) + '")>';
2884 - }
2885 - x += '</div><br />';
2886 - setDialogMode(2, "Manage Backup Codes", 8, null, x, 'otpauth-manage');
2887 - break;
2888 - }
2889 - case 'otp-hkey-get': {
2890 - if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) return;
2891 - var start = '<div style="border-radius:6px;border:2px solid #CCC;background-color:#BBB;width:100%;box-sizing:border-box;margin-bottom:6px"><div style="margin:3px;font-family:Arial, Helvetica, sans-serif;font-size:16px;font-weight:bold"><table style=width:100%;text-align:left>';
2892 - var end = '</table></div></div>';
2893 - var x = "<a href=\"https://www.yubico.com/\" rel=\"noreferrer noopener\" target=\"_blank\">Hardware keys</a> are used as secondary login authentication.";
2894 - x += '<div style="max-height:150px;overflow-y:auto;overflow-x:hidden;margin-top:6px;margin-bottom:6px">';
2895 - if (message.keys && message.keys.length > 0) {
2896 - for (var i in message.keys) {
2897 - var key = message.keys[i], type = (key.type == 2)?'OTP':'WebAuthn';
2898 - x += start + '<tr style=margin:5px><td style=width:30px><img width=24 height=18 src="images/hardware-key-' + type + '-24.png" style=margin-top:4px><td style=width:250px>' + key.name + '<td><input type=button value="' + "Remove" + '" onclick=account_removehkey(' + key.i + ')></input>' + end;
2899 - }
2900 - } else {
2901 - x += start + '<tr style=text-align:center><td>' + "No Keys Configured" + end;
2902 - }
2903 - x += '</div>';
2904 - x += '<div><input type=button value="' + "Close" + '" onclick=setDialogMode(0) style=float:right></input>';
2905 - var hkeycount = (typeof userinfo.otphkeys == 'number') ? userinfo.otphkeys : 0;
2906 - if ((typeof serverinfo.maxfidokeys != 'number') || (serverinfo.maxfidokeys > hkeycount)) { // Check if we we reached maximum hardware keys
2907 - if ((features & 0x00020000) != 0) { x += '<input id=d2addkey3 type=button value="' + "Add Key" + '" onclick="account_addhkey(3);"></input>'; }
2908 - if ((features & 0x00004000) != 0) { x += '<input id=d2addkey2 type=button value="' + "Add YubiKey&reg; OTP" + '" onclick="account_addhkey(2);"></input>'; }
2909 - } else {
2910 - x += "Maximum keys reached.";
2911 - }
2912 - x += '</div><br />';
2913 - setDialogMode(2, "Manage Security Keys", 8, null, x, 'otpauth-hardware-manage');
2914 - if (u2fSupported() == false) { QE('d2addkey1', false); }
2915 - break;
2916 - }
2917 - case 'otp-hkey-yubikey-add': {
2918 - if (message.result) {
2919 - meshserver.send({ action: 'otp-hkey-get' }); // Success, ask for the full list of keys.
2920 - } else {
2921 - setDialogMode(2, "Add Security Key", 1, null, '<br />' + "Error, Unable to add key." + '<br /><br />');
2922 - }
2923 - break;
2924 - }
2925 - case 'otp-hkey-setup-response': {
2926 - if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) return;
2927 - if (message.result == true) {
2928 - meshserver.send({ action: 'otp-hkey-get' }); // Success, ask for the full list of keys.
2929 - } else {
2930 - setDialogMode(2, "Add Security Key", 1, null, '<br />' + "ERROR: Unable to add key." + '<br /><br />', 'otpauth-hardware-manage');
2931 - }
2932 - break;
2933 - }
2934 - case 'webauthn-startregister': {
2935 - if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) return;
2936 - var x = "Press the key button now." + '<br /><br /><div style=width:100%;text-align:center><img width=120 height=117 src="images/hardware-keypress-120.png" /></div><input id=dp1keyname style=display:none value=' + message.name + ' />';
2937 - setDialogMode(2, "Add Security Key", 2, null, x);
2938 -
2939 - var publicKey = message.request;
2940 - message.request.challenge = Uint8Array.from(atob(message.request.challenge), function (c) { return c.charCodeAt(0) })
2941 - message.request.user.id = Uint8Array.from(atob(message.request.user.id), function (c) { return c.charCodeAt(0) })
2942 - navigator.credentials.create({ publicKey: publicKey }).then(function(newCredentialInfo) {
2943 - // Public key credential
2944 - meshserver.send({ action: 'webauthn-endregister', response: { rawId: btoa(String.fromCharCode.apply(null, new Uint8Array(newCredentialInfo.rawId))), response: { attestationObject: btoa(String.fromCharCode.apply(null, new Uint8Array(newCredentialInfo.response.attestationObject))), clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(newCredentialInfo.response.clientDataJSON))) }, type: newCredentialInfo.type } });
2945 - setDialogMode(0);
2946 - }, function(error) {
2947 - // Error
2948 - console.log("ERROR: " + error);
2949 - setDialogMode(2, "Add Security Key", 1, null, "ERROR: " + error);
2950 - });
2951 - break;
2952 - }
2953 - case 'verifyPhone': {
2954 - if (xxdialogMode && (xxdialogTag != 'verifyPhone')) return;
2955 - var x = '<table><tr><td><img src="images/phone80.png" style=padding:8px>';
2956 - x += '<td>' + "Check your phone and enter the verification code.";
2957 - x += '<br /><br /><div style=width:100%;text-align:center>' + "Verification code:" + ' <input type=tel pattern="[0-9]" inputmode="number" maxlength=6 id=d2phoneCodeInput onKeyUp=account_managePhoneCodeValidate() onkeypress="if (event.key==\'Enter\') account_managePhoneCodeValidate(1)"></div></table>';
2958 - setDialogMode(2, "Phone Notifications", 3, account_managePhoneConfirm, x, message.cookie);
2959 - Q('d2phoneCodeInput').focus();
2960 - account_managePhoneCodeValidate();
2961 - break;
2962 - }
2963 - case 'verifyMessaging': {
2964 - if (xxdialogMode && (xxdialogTag != 'verifyMessaging')) return;
2965 - var x = '<table><tr><td><img src="images/messaging40.png" style=padding:8px>';
2966 - x += '<td>' + "Check your messaging application and enter the verification code.";
2967 - x += '<br /><br /><div style=width:100%;text-align:center>' + "Verification code:" + ' <input type=tel pattern="[0-9]" inputmode="number" maxlength=6 id=d2phoneCodeInput onKeyUp=account_managePhoneCodeValidate() onkeypress="if (event.key==\'Enter\') account_managePhoneCodeValidate(1)"></div></table>';
2968 - setDialogMode(2, "Messaging Notifications", 3, account_manageMessagingConfirm, x, message.cookie);
2969 - Q('d2phoneCodeInput').focus();
2970 - account_managePhoneCodeValidate();
2971 - break;
2972 - }
2973 - case 'fileoperation': {
2974 - // View the file in the dialog box
2975 - var p5editSaveBack = function(b, tag) {
2976 - var data;
2977 - if (d4EditEncodingVal == 1) {
2978 - data = encode_utf8(Q('d4editorarea').value); // UTF8 encoding
2979 - } else {
2980 - data = Q('d4editorarea').value; // RAW encoding
2981 - }
2982 - meshserver.send({ action: 'fileoperation', fileop: 'set', path: tag.path, file: tag.file, data: btoa(data) });
2983 - }
2984 - setDialogMode(4, EscapeHtml(message.file), 3, p5editSaveBack, null, message);
2985 - QS('dialog').width = 'auto';
2986 - QS('dialog').bottom = '80px';
2987 - QS('dialog').top = QS('dialog').left = QS('dialog').right = '100px';
2988 - if (d4EditEncodingVal == 1) {
2989 - Q('d4editorarea').value = decode_utf8(atob(message.data)); // UTF8 Encoding
2990 - } else {
2991 - Q('d4editorarea').value = atob(message.data); // RAW Encoding
2992 - }
2993 - break;
2994 - }
2995 - case 'event': {
2996 - if (!message.event.nolog) {
2997 - if (currentNode && (message.event.nodeid == currentNode._id) && (currentDeviceEvents != null)) {
2998 - // If this event has a nodeid and we are looking at this node, update the log in real time.
2999 - currentDeviceEvents.unshift(message.event);
3000 - var eventLimit = parseInt(p16limitdropdown.value);
3001 - while (currentDeviceEvents.length > eventLimit) { currentDeviceEvents.pop(); } // Remove element(s) at the end
3002 - mainUpdate(1024);
3003 - }
3004 -
3005 - if (currentUser && (message.event.userid == currentUser._id)) {
3006 - // If this event has a userid and we are looking at this user, update the log in real time.
3007 - currentUserEvents.unshift(message.event);
3008 - var eventLimit = parseInt(p31limitdropdown.value);
3009 - while (currentUserEvents.length > eventLimit) { currentUserEvents.pop(); } // Remove element(s) at the end
3010 - mainUpdate(2048);
3011 - }
3012 -
3013 - // Add this event to the main events log.
3014 - events.unshift(message.event);
3015 - var eventLimit = parseInt(p3limitdropdown.value);
3016 - while (events.length > eventLimit) { events.pop(); } // Remove element(s) at the end
3017 - mainUpdate(32);
3018 - }
3019 - if (message.event.noact) break; // Take no action on this event
3020 -
3021 - switch (message.event.action) {
3022 - case 'serverinfochange': {
3023 - if (message.event.lock2factor != null) { serverinfo.lock2factor = message.event.lock2factor; updateSelf(); updateSiteAdmin(); }
3024 - break;
3025 - }
3026 - case 'deviceShareUpdate': {
3027 - if (message.event.nodeid != deviceSharesReq) break;
3028 - deviceSharesNode = message.event.nodeid;
3029 - deviceShares = message.event.deviceShares;
3030 - if (currentNode._id == deviceSharesNode) { gotoDevice(currentNode._id, xxcurrentView, true); }
3031 - break;
3032 - }
3033 - case 'agentlog': {
3034 - if (message.event.msgid == 98) {
3035 - // This is a agent help request, popup a notification.
3036 - if (GetNodeRights(message.event.nodeid) > 0) {
3037 - var n = getNodeFromId(message.event.nodeid);
3038 - addNotification({ text: format("Help requested from {0}: {1}", message.event.msgArgs[0], message.event.msgArgs[1]), title: n.name, icon: n.icon, nodeid: message.event.nodeid });
3039 - }
3040 - }
3041 - break;
3042 - }
3043 - case 'recording': {
3044 - if ((p52recordings != null) && (typeof p52recordings == 'object')) { p52recordings.unshift(message.event); message.event.present = 1; updateRecordings(); }
3045 - break;
3046 - }
3047 - case 'userWebState': {
3048 - // New user web state, update the web page as needed
3049 - try {
3050 - if (localStorage != null) {
3051 - // TODO: The problem with this "old" values is that if changed from a different tab, the old and new values will be the same.
3052 - // So comparing against these values is not a good idea.
3053 - var oldShowRealNames = localStorage.getItem('showRealNames');
3054 - var oldUiMode = localStorage.getItem('uiMode');
3055 - var oldSort = localStorage.getItem('sort');
3056 - var oldLoctag = localStorage.getItem('loctag');
3057 - var oldNightMode = localStorage.getItem('nightMode');
3058 - var oldFooterBar = localStorage.getItem('footerBar');
3059 -
3060 - var webstate = JSON.parse(message.event.state);
3061 - for (var i in webstate) { localStorage.setItem(i, webstate[i]); }
3062 -
3063 - // Update the web page
3064 - //if ((webstate.deskAspectRatio != null) && (webstate.deskAspectRatio != deskAspectRatio)) { deskAspectRatio = webstate.deskAspectRatio; deskAdjust(); }
3065 - if ((webstate.showRealNames != null) && (webstate.showRealNames != oldShowRealNames)) { showRealNames = Q('RealNameCheckBox').checked = (webstate.showRealNames == '1'); mainUpdate(6); }
3066 - if ((webstate.uiMode != null) && (webstate.uiMode != oldUiMode)) { userInterfaceSelectMenu(parseInt(webstate.uiMode)); }
3067 - if ((webstate.sort != null) && (webstate.sort != oldSort)) { document.getElementById('sortselect').selectedIndex = sort = parseInt(webstate.sort); mainUpdate(6); }
3068 - if ((webstate.loctag != null) && (webstate.loctag != oldLoctag)) { if (webstate.loctag != null) { args.locale = webstate.loctag; } else { delete args.locale; } mainUpdate(0xFFFFFFFF); }
3069 - if ((webstate.nightMode != null) && (webstate.nightMode != oldNightMode)) { putstore('nightMode', webstate.nightMode); setNightMode(); }
3070 - if ((webstate.footerBar != null) && (webstate.footerBar != oldFooterBar)) { footerBar = (webstate.footerBar == '1'); QS('container')['grid-template-rows'] = null; QS('container')['-ms-grid-rows'] = null; adjustPanels(); }
3071 - if ((webstate.stars != null) && (webstate.stars != JSON.stringify(stars))) {
3072 - stars = JSON.parse(webstate.stars);
3073 - if (Q('DevFilterSelect').value == 3) { mainUpdate(5); } else { mainUpdate(4); }
3074 - if (currentNode) { refreshDevice(currentNode._id); }
3075 - }
3076 - if (webstate.deskKeyShortcuts != null) {
3077 - // Set the user's desktop shortcut keys
3078 - deskKeyboardShortcuts = [];
3079 - var deskKeyboardShortcutsStr = webstate.deskKeyShortcuts.split(',');
3080 - for (var i in deskKeyboardShortcutsStr) { deskKeyboardShortcuts.push(parseInt(deskKeyboardShortcutsStr[i])); }
3081 - updateDeskShortcutKeys();
3082 - }
3083 - if (webstate.deskStrings != null) {
3084 - // Set the user's desktop strings
3085 - var s = null;
3086 - try { s = JSON.parse(webstate.deskStrings); } catch (ex) {}
3087 - if (Array.isArray(s)) { deskKeyboardStrings = s; updateDesktopStrings(); }
3088 - }
3089 - }
3090 - } catch (ex) {}
3091 - break;
3092 - }
3093 - case 'servertimelinestats': { addServerTimelineStats(message.event.data); break; }
3094 - case 'accountcreate':
3095 - case 'accountchange': {
3096 - // An account was created or changed
3097 - if ((typeof message.event.account != 'object') || (message.event.account == null)) { console.log(message.event); return; }
3098 - if (userinfo._id == message.event.account._id) {
3099 - var newsiteadmin = message.event.account.siteadmin?message.event.account.siteadmin:0;
3100 - var oldsiteadmin = userinfo.siteadmin?userinfo.siteadmin:0;
3101 - var newRemoveRights = message.event.account.removeRights?message.event.account.removeRights:0;
3102 - var oldRemoveRights = userinfo.removeRights?userinfo.removeRights:0;
3103 - if ((message.event.account.quota != userinfo.quota) || (((userinfo.siteadmin & 8) == 0) && ((message.event.account.siteadmin & 8) != 0))) { meshserver.send({ action: 'files' }); }
3104 - var oldgroups = userinfo.groups;
3105 - userinfo = message.event.account;
3106 - if ((oldsiteadmin != newsiteadmin) || (oldRemoveRights != newRemoveRights) || (message.event.accountImageChange == 1)) { // If the site admin permission or user image has changed...
3107 - if (message.event.accountImageChange == 1) { userinfo.accountImageRnd = Math.floor(Math.random() * 9999999999); }
3108 - updateSiteAdmin();
3109 - }
3110 - updateSelf();
3111 -
3112 - if ((userinfo.siteadmin & 2) != 0) {
3113 - // Compare our groups
3114 - var og = oldgroups ? oldgroups : [];
3115 - var ng = userinfo.groups ? userinfo.groups : [];
3116 - if (og.join(',') != ng.join(',')) {
3117 - // Our groups have changed, re-ask for a list of users.
3118 - users = wssessions = null;
3119 - meshserver.send({ action: 'users' });
3120 - meshserver.send({ action: 'wssessioncount' });
3121 - }
3122 - }
3123 -
3124 - // If our list of nodes may have changes, request the new list now.
3125 - if (message.event.nodeListChange == userinfo._id) { meshserver.send({ action: 'nodes', skip: (devicePagingState == null) ? 0 : devicePagingState.skip }); }
3126 - }
3127 - if (currentNode) { refreshDevice(currentNode._id); }
3128 - if (users == null) break;
3129 -
3130 - // Check if the account is part of our user group
3131 - if ((userinfo.groups == null) || (userinfo.groups.length == 0) || (findOne(message.event.account.groups, userinfo.groups) == true)) {
3132 - users[message.event.account._id] = message.event.account; // Part of our groups, update this user.
3133 - } else {
3134 - delete users[message.event.account._id]; // No longer part of our groups, remove this user.
3135 - }
3136 -
3137 - mainUpdate(4 | 16384);
3138 - break;
3139 - }
3140 - case 'accountremove': {
3141 - // An account was removed
3142 - if (users == null) break;
3143 - delete users[message.event.userid];
3144 - mainUpdate(16384);
3145 - break;
3146 - }
3147 - case 'createusergroup':
3148 - case 'usergroupchange': {
3149 - // User group changed
3150 - if (usergroups == null) { usergroups = {}; }
3151 - var ugroup = usergroups[message.event.ugrpid];
3152 - if (ugroup == null) {
3153 - // This is a new user group for us
3154 - usergroups[message.event.ugrpid] = { _id: message.event.ugrpid, name: message.event.name, desc: message.event.desc, domain: message.event.domain, links: message.event.links };
3155 - } else {
3156 - // This is an existing user group
3157 - ugroup.name = message.event.name;
3158 - if (message.event.desc) { ugroup.desc = message.event.desc; } else { delete ugroup.desc; }
3159 - if (message.event.links) { ugroup.links = message.event.links; } else { delete ugroup.links; }
3160 - if (message.event.flags) { ugroup.flags = message.event.flags; } else { delete ugroup.flags; }
3161 - if (typeof message.event.consent == 'number') { ugroup.consent = message.event.consent; }
3162 - }
3163 - mainUpdate(4096 + 8192 + 16384);
3164 -
3165 - // Group update, refresh all our device groups and nodes. TODO: Optimize this to only do this when needed.
3166 - meshserver.send({ action: 'meshes' });
3167 - meshserver.send({ action: 'nodes', skip: (devicePagingState == null) ? 0 : devicePagingState.skip });
3168 - break;
3169 - }
3170 - case 'deleteusergroup': {
3171 - // User group removed
3172 - if ((usergroups != null) && (usergroups[message.event.ugrpid] != null)) {
3173 - delete usergroups[message.event.ugrpid];
3174 - var c = 0;
3175 - for (var i in usergroups) { c++; }
3176 - if (c == 0) { usergroups = null; } // If user groups is empty, set it to null.
3177 - mainUpdate(8192 + 16384);
3178 - }
3179 - break;
3180 - }
3181 - case 'createmesh': {
3182 - // A new mesh was created
3183 - if ((meshes[message.event.meshid] == null) && ((serverinfo.manageAllDeviceGroups) || (message.event.mesh.links[userinfo._id] != null))) { // Check if this is a mesh create for a mesh we own. If site administrator, we get all messages so need to ignore some.
3184 - meshes[message.event.meshid] = message.event.mesh;
3185 - mainUpdate(4 + 128 + 8192 + 16384);
3186 - meshserver.send({ action: 'files' });
3187 - }
3188 - break;
3189 - }
3190 - case 'meshchange': {
3191 - // Update mesh information
3192 - if (meshes[message.event.meshid] == null) {
3193 - // Check if we have any access to this device group
3194 - var add = false;
3195 - if (message.event.links[userinfo._id] != null) { add = true; }
3196 - if (userinfo.links[message.event.meshid] != null) { add = true; }
3197 - for (var i in userinfo.links) { if ((i.startsWith('ugrp/')) && (message.event.links[i] != null)) { add = true; } }
3198 -
3199 - // This is a new mesh for us
3200 - if (add) {
3201 - meshes[message.event.meshid] = { _id: message.event.meshid, name: message.event.name, mtype: message.event.mtype, desc: message.event.desc, links: message.event.links, amt: message.event.amt, invite: message.event.invite, expireDevs: message.event.expireDevs, relayid: message.event.relayid };
3202 - meshserver.send({ action: 'nodes', skip: (devicePagingState == null) ? 0 : devicePagingState.skip }); // Request a refresh of all nodes (TODO: We could optimize this to only request nodes for the new mesh).
3203 - }
3204 - } else {
3205 - // This is an existing device group
3206 - if (message.event.name != null) {
3207 - meshes[message.event.meshid].name = message.event.name;
3208 - for (var i in nodes) { if (nodes[i].meshid == message.event.meshid) { nodes[i].meshnamel = message.event.name.toLowerCase(); } }
3209 - }
3210 - if (message.event.desc != null) { meshes[message.event.meshid].desc = message.event.desc; }
3211 - if (message.event.flags != null) { meshes[message.event.meshid].flags = message.event.flags; }
3212 - if (message.event.consent != null) { meshes[message.event.meshid].consent = message.event.consent; }
3213 - if (message.event.links) { meshes[message.event.meshid].links = message.event.links; }
3214 - if (message.event.amt) { meshes[message.event.meshid].amt = message.event.amt; }
3215 - if (message.event.invite != null) { meshes[message.event.meshid].invite = message.event.invite; } else { delete meshes[message.event.meshid].invite; }
3216 - if (message.event.expireDevs != null) { if (message.event.expireDevs > 0) { meshes[message.event.meshid].expireDevs = message.event.expireDevs; } else { delete meshes[message.event.meshid].expireDevs; } }
3217 - if (message.event.relayid != null) { meshes[message.event.meshid].relayid = message.event.relayid; }
3218 -
3219 - // Check if we lost rights to this mesh in this change.
3220 - if (IsMeshViewable(message.event.meshid) == false) {
3221 - if ((xxcurrentView == 20) && (currentMesh == meshes[message.event.meshid])) go(2);
3222 - delete meshes[message.event.meshid];
3223 -
3224 - // Delete all nodes in that mesh, except ones with direct links
3225 - var newnodes = [];
3226 - for (var i in nodes) { if ((nodes[i].meshid != message.event.meshid) || ((userinfo.links != null) && (userinfo.links[nodes[i]._id] != null))) { newnodes.push(nodes[i]); } }
3227 - nodes = newnodes;
3228 -
3229 - // If we are looking at a node that is no longer visible, move back to "My Devices"
3230 - if ((xxcurrentView >= 10) && (xxcurrentView < 20) && currentNode && !IsNodeViewable(currentNode)) { setDialogMode(0); go(1); }
3231 - }
3232 - }
3233 - mainUpdate(4 + 128 + 8192 + 16384);
3234 - if (currentNode && !IsNodeViewable(currentNode)) { currentNode = null; if ((xxcurrentView >= 10) && (xxcurrentView < 20)) { go(1); } }
3235 - //meshserver.send({ action: 'files' }); // TODO: Why do we need to do this??
3236 -
3237 - // If we are looking at a mesh that is now deleted, move back to "My Account"
3238 - if (xxcurrentView == 20 && currentMesh._id == message.event.meshid) { mainUpdate(4096); }
3239 - break;
3240 - }
3241 - case 'deletemesh': {
3242 - // Delete the mesh
3243 - if (meshes[message.event.meshid]) {
3244 - delete meshes[message.event.meshid];
3245 - mainUpdate(128);
3246 - meshserver.send({ action: 'files' });
3247 - }
3248 -
3249 - // Delete all nodes in that mesh
3250 - var newnodes = [];
3251 - if (nodes != null) { for (var i in nodes) { if (nodes[i].meshid != message.event.meshid) { newnodes.push(nodes[i]); } } }
3252 - nodes = newnodes;
3253 - mainUpdate(4 + 8192 + 16384);
3254 -
3255 - // If we are looking at a mesh that is now deleted, move back to "My Account"
3256 - if (xxcurrentView >= 20 && xxcurrentView < 30 && currentMesh._id == message.event.meshid) { setDialogMode(0); go(2); }
3257 - // If we are looking at a node in the deleted mesh, move back to "My Devices"
3258 - if (xxcurrentView >= 10 && xxcurrentView < 20 && currentNode && !IsNodeViewable(currentNode)) { setDialogMode(0); go(1); }
3259 - break;
3260 - }
3261 - case 'addnode': {
3262 - var node = message.event.node;
3263 - if (!meshes[node.meshid]) break; // This is a node for a mesh we don't know. Happens when we are site administrator, we get all messages.
3264 - if (getNodeFromId(node._id) != null) break; // This node is already known.
3265 - node.namel = node.name.toLowerCase();
3266 - if (node.rname) { node.rnamel = node.rname.toLowerCase(); } else { node.rnamel = node.namel; }
3267 - node.meshnamel = meshes[node.meshid]?meshes[node.meshid].name.toLowerCase():'*';
3268 - node.state = 0;
3269 - if (!node.icon) node.icon = 1;
3270 - node.ident = ++nodeShortIdent;
3271 - if (nodes == null) { }
3272 - nodes.push(node);
3273 -
3274 - // Web page update
3275 - mainUpdate(1 | 2 | 4 | 16);
3276 -
3277 - // If we are looking at the device group for this device, update that.
3278 - if ((xxcurrentView == 20) && (currentMesh != null) && (currentMesh._id == node.meshid)) { mainUpdate(4096); }
3279 -
3280 - break;
3281 - }
3282 - case 'removenode': {
3283 - var index = -1;
3284 - for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } }
3285 - if (index != -1) {
3286 - var node = nodes[index];
3287 - if (currentNode == node) {
3288 - if (xxcurrentView >= 10 && xxcurrentView < 20) { setDialogMode(0); go(1); }
3289 - currentNode = null;
3290 - // TODO: Correctly disconnect from this node (Desktop/Terminal/Files...)
3291 - }
3292 - nodes.splice(index, 1);
3293 -
3294 - // Web page update
3295 - mainUpdate(4 | 16);
3296 -
3297 - // If we are looking at the device group for this device, update that.
3298 - if ((xxcurrentView == 20) && (currentMesh != null) && (currentMesh._id == node.meshid)) { mainUpdate(4096); }
3299 - }
3300 - break;
3301 - }
3302 - case 'changenode': {
3303 - var index = -1;
3304 - for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } }
3305 - if (index != -1) {
3306 - var node = nodes[index];
3307 -
3308 - // Change the node
3309 - node.name = message.event.node.name;
3310 - node.rname = message.event.node.rname;
3311 - node.users = message.event.node.users;
3312 - node.host = message.event.node.host;
3313 - node.desc = message.event.node.desc;
3314 - node.ip = message.event.node.ip;
3315 - node.osdesc = message.event.node.osdesc;
3316 - node.publicip = message.event.node.publicip;
3317 - node.iploc = message.event.node.iploc;
3318 - node.wifiloc = message.event.node.wifiloc;
3319 - node.gpsloc = message.event.node.gpsloc;
3320 - node.tags = message.event.node.tags;
3321 - node.ssh = message.event.node.ssh;
3322 - node.rdp = message.event.node.rdp;
3323 - node.userloc = message.event.node.userloc;
3324 - node.rdpport = message.event.node.rdpport;
3325 - node.rfbport = message.event.node.rfbport;
3326 - node.sshport = message.event.node.sshport;
3327 - node.httpport = message.event.node.httpport;
3328 - node.httpsport = message.event.node.httpsport;
3329 - node.consent = message.event.node.consent;
3330 - node.pmt = message.event.node.pmt;
3331 - if (message.event.node.links != null) { node.links = message.event.node.links; } else { delete node.links; }
3332 - if (message.event.node.agent != null) {
3333 - if (node.agent == null) node.agent = {};
3334 - if (message.event.node.agent.ver != null) { node.agent.ver = message.event.node.agent.ver; }
3335 - if (message.event.node.agent.id != null) { node.agent.id = message.event.node.agent.id; }
3336 - if (message.event.node.agent.caps != null) { node.agent.caps = message.event.node.agent.caps; }
3337 - if (message.event.node.agent.root != null) { node.agent.root = message.event.node.agent.root; }
3338 - if (message.event.node.agent.core != null) { node.agent.core = message.event.node.agent.core; } else { if (node.agent.core) { delete node.agent.core; } }
3339 - node.agent.tag = message.event.node.agent.tag;
3340 - }
3341 - if (message.event.node.intelamt != null) {
3342 - if (node.intelamt == null) node.intelamt = {};
3343 - if (message.event.node.intelamt.state != null) { node.intelamt.state = message.event.node.intelamt.state; }
3344 - if (message.event.node.intelamt.host != null) { node.intelamt.user = message.event.node.intelamt.host; }
3345 - if (message.event.node.intelamt.user != null) { node.intelamt.user = message.event.node.intelamt.user; } else { delete node.intelamt.user; }
3346 - if (message.event.node.intelamt.tls != null) { node.intelamt.tls = message.event.node.intelamt.tls; }
3347 - if (message.event.node.intelamt.ver != null) { node.intelamt.ver = message.event.node.intelamt.ver; }
3348 - if (message.event.node.intelamt.tag != null) { node.intelamt.tag = message.event.node.intelamt.tag; }
3349 - if (message.event.node.intelamt.uuid != null) { node.intelamt.uuid = message.event.node.intelamt.uuid; }
3350 - if (message.event.node.intelamt.realm != null) { node.intelamt.realm = message.event.node.intelamt.realm; }
3351 - if (message.event.node.intelamt.flags != null) { node.intelamt.flags = message.event.node.intelamt.flags; }
3352 - if (message.event.node.intelamt.warn != null) { node.intelamt.warn = message.event.node.intelamt.warn; } else { delete node.intelamt.warn; }
3353 - }
3354 - if (message.event.node.av != null) { node.av = message.event.node.av; }
3355 - if (message.event.node.wsc != null) { node.wsc = message.event.node.wsc; }
3356 - node.namel = node.name.toLowerCase();
3357 - if (node.rname) { node.rnamel = node.rname.toLowerCase(); } else { node.rnamel = node.namel; }
3358 - if (message.event.node.icon) { node.icon = message.event.node.icon; }
3359 -
3360 - // Check if this device has changed device group
3361 - if (message.event.node.meshid != node.meshid) {
3362 - if ((meshes[message.event.node.meshid] == null) && ((userinfo.links == null) || (userinfo.links[node._id] == null))) {
3363 - // We don't see the new mesh, remove this device
3364 - // TODO: Correctly disconnect from this node (Desktop/Terminal/Files...)
3365 - if ((currentNode != null) && (currentNode._id == node._id)) {
3366 - if ((xxcurrentView >= 10) && (xxcurrentView < 20) && !IsNodeViewable(currentNode)) { currentNode = null; setDialogMode(0); go(1); }
3367 - }
3368 - var index = -1;
3369 - for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } }
3370 - nodes.splice(index, 1);
3371 -
3372 - mainUpdate(4 | 16);
3373 - } else {
3374 - // We see the new mesh, move this device
3375 - node.meshid = message.event.node.meshid;
3376 - node.meshnamel = meshes[message.event.node.meshid]?meshes[message.event.node.meshid].name.toLowerCase():'*';
3377 - mainUpdate(1 | 2 | 4);
3378 - }
3379 - }
3380 -
3381 - // Web page update
3382 - updateDeviceViewDevice(node);
3383 - mainUpdate(2 | 8 | 16);
3384 - refreshDevice(node._id);
3385 -
3386 - // If we are looking at the device group for this device, update that.
3387 - if ((xxcurrentView == 20) && (currentMesh != null) && (currentMesh._id == node.meshid)) { mainUpdate(4096); }
3388 -
3389 - if ((currentNode == node) && (xxdialogMode != null) && (xxdialogTag == '@xxmap')) { p10showNodeLocationDialog(); }
3390 - }
3391 - break;
3392 - }
3393 - case 'nodemeshchange': {
3394 - var index = -1;
3395 - for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } }
3396 - if (index != -1) {
3397 - var node = nodes[index];
3398 - if ((meshes[message.event.newMeshId] == null) && ((userinfo.links == null) || (userinfo.links[node._id] == null))) {
3399 - // We don't see the new mesh, remove this device
3400 -
3401 - // TODO: Correctly disconnect from this node (Desktop/Terminal/Files...)
3402 - if ((currentNode != null) && (currentNode._id == node._id)) {
3403 - if ((xxcurrentView >= 10) && (xxcurrentView < 20) && !IsNodeViewable(currentNode)) { currentNode = null; setDialogMode(0); go(1); }
3404 - }
3405 - nodes.splice(index, 1);
3406 - mainUpdate(4 | 16);
3407 - } else {
3408 - // We see the new mesh, move this device
3409 - node.meshid = message.event.newMeshId;
3410 - node.meshnamel = meshes[message.event.newMeshId]?meshes[message.event.newMeshId].name.toLowerCase():'*';
3411 - mainUpdate(1 | 2 | 4);
3412 - }
3413 - refreshDevice(message.event.nodeid);
3414 - } else {
3415 - // This is a new device, add it.
3416 - var node = message.event.node;
3417 - if (!meshes[node.meshid]) break; // This is a node for a mesh we don't know. Happens when we are site administrator, we get all messages.
3418 - node.namel = node.name.toLowerCase();
3419 - if (node.rname) { node.rnamel = node.rname.toLowerCase(); } else { node.rnamel = node.namel; }
3420 - node.meshnamel = meshes[node.meshid]?meshes[node.meshid].name.toLowerCase():'*';
3421 - node.state = 0;
3422 - if (!node.icon) node.icon = 1;
3423 - node.ident = ++nodeShortIdent;
3424 - if (nodes == null) { }
3425 - nodes.push(node);
3426 -
3427 - // Web page update
3428 - mainUpdate(1 | 2 | 4 | 16);
3429 - }
3430 - break;
3431 - }
3432 - case 'nodeconnect': {
3433 - // Indicated a node has changed connectivity state
3434 - var index = -1;
3435 - for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } }
3436 - if (index != -1) {
3437 - var node = nodes[index];
3438 -
3439 - // Event the connection change if needed
3440 - var n = getstore('notifications', 0); // Account notification settings
3441 -
3442 - // Per-group notification settings
3443 - if (message.event.meshid && userinfo.links && userinfo.links[message.event.meshid] && userinfo.links[message.event.meshid].notify) {
3444 - n |= userinfo.links[message.event.meshid].notify;
3445 - }
3446 -
3447 - // Per-user notification settings
3448 - if (userinfo.notify != null) {
3449 - if (userinfo.notify[message.event.meshid] != null) { n |= userinfo.notify[message.event.meshid]; }
3450 - if (userinfo.notify[message.event.nodeid] != null) { n |= userinfo.notify[message.event.nodeid]; }
3451 - }
3452 -
3453 - // Show the notification
3454 - if (n & 2) {
3455 - var agentPrivilages = "Agent connected";
3456 - if ((node.agent != null) && (node.agent.root === false)) { "Agent connected with limited privilages"; }
3457 - if (((node.conn & 1) == 0) && ((message.event.conn & 1) != 0)) { addNotification({ text: agentPrivilages, title: node.name, icon: node.icon, nodeid: node._id }); }
3458 - if (((node.conn & 2) == 0) && ((message.event.conn & 2) != 0)) { addNotification({ text: "Intel AMT detected", title: node.name, icon: node.icon, nodeid: node._id }); }
3459 - if (((node.conn & 4) == 0) && ((message.event.conn & 4) != 0)) { addNotification({ text: "Intel AMT CIRA connected", title: node.name, icon: node.icon, nodeid: node._id }); }
3460 - if (((node.conn & 16) == 0) && ((message.event.conn & 16) != 0)) { addNotification({ text: "MQTT connected", title: node.name, icon: node.icon, nodeid: node._id }); }
3461 - }
3462 - if (n & 4) {
3463 - if (((node.conn & 1) != 0) && ((message.event.conn & 1) == 0)) { addNotification({ text: "Agent disconnected", title: node.name, icon: node.icon, nodeid: node._id }); }
3464 - if (((node.conn & 2) != 0) && ((message.event.conn & 2) == 0)) { addNotification({ text: "Intel AMT not detected", title: node.name, icon: node.icon, nodeid: node._id }); }
3465 - if (((node.conn & 4) != 0) && ((message.event.conn & 4) == 0)) { addNotification({ text: "Intel AMT CIRA disconnected", title: node.name, icon: node.icon, nodeid: node._id }); }
3466 - if (((node.conn & 16) != 0) && ((message.event.conn & 16) == 0)) { addNotification({ text: "MQTT disconnected", title: node.name, icon: node.icon, nodeid: node._id }); }
3467 - }
3468 -
3469 - // Change the node connection state
3470 - node.conn = message.event.conn;
3471 - node.pwr = message.event.pwr;
3472 - node.lastconnect = Date.now();
3473 -
3474 - // Clear sesssion and battery information if needed
3475 - if ((node.conn & 1) == 0) { delete node.sessions; }
3476 -
3477 - // Web page update
3478 - var filter = Q('DevFilterSelect').value;
3479 - if ((filter == 1) || (filter == 5)) {
3480 - // We are looking at online or offline devices. This may change that state.
3481 - mainUpdate(1 | 4 | 16);
3482 - } else {
3483 - // We are looking at a different filter, just update that specific device.
3484 - updateDeviceViewDevice(node);
3485 - mainUpdate(1 | 16);
3486 - }
3487 - refreshDevice(node._id);
3488 -
3489 - // If we are looking at the device group for this device, update that.
3490 - if ((xxcurrentView == 20) && (currentMesh != null) && (currentMesh._id == node.meshid)) { mainUpdate(4096); }
3491 - }
3492 - break;
3493 - }
3494 - case 'wssessioncount': {
3495 - // Update the active web socket session count for a user
3496 - if (wssessions != null) {
3497 - if (message.event.count == 0 && wssessions[message.event.userid]) {
3498 - delete wssessions[message.event.userid];
3499 - } else {
3500 - wssessions[message.event.userid] = message.event.count;
3501 - }
3502 - mainUpdate(16384);
3503 - }
3504 - break;
3505 - }
3506 - case 'login': {
3507 - // Update the last login time
3508 - if (users != null && users[message.event.userid]) {
3509 - users[message.event.userid].login = Math.floor(new Date(message.event.time).getTime() / 1000);
3510 - }
3511 - break;
3512 - }
3513 - case 'scanamtdevice': {
3514 - // Populate the Intel AMT scan dialog box with the result of the RMCP scan
3515 - if ((xxdialogMode == null) || (!Q('dp1range')) || (xxdialogTag != ('AMTSCAN:' + message.event.range))) return;
3516 - var x = '';
3517 - if (message.event.results == null) {
3518 - // The scan could not occur because of an error. Likely the user range was invalid.
3519 - x = '<div style=width:100%;text-align:center;margin-top:12px>' + "Unable to scan this address range." + '</div><div style=width:100%;text-align:center;margin-top:12px;color:gray;line-height:1.5>' + "Sample IP range values" + '<br />192.168.0.100<br />192.168.1.0/24<br />192.167.0.1-192.168.0.100</div>';
3520 - } else {
3521 - // Go thru all the results and populate the dialog box
3522 - amtScanResults = message.event.results;
3523 - for (var i in message.event.results) {
3524 - var r = message.event.results[i], shortname = r.hostname;
3525 - if (r.hosttype == 'host') { shortname = capitalizeFirstLetter(shortname.split('.')[0]); }
3526 - if (shortname.length > 20) { shortname = shortname.substring(0, 20) + '...'; }
3527 - var str = '<b title="' + EscapeHtml(r.hostname) + '">' + EscapeHtml(shortname) + '</b> - v' + r.ver;
3528 - if (r.state == 2) { if (r.tls == 1) { str += " with TLS."; } else { str += " without TLS."; } } else { str += ' not activated.'; }
3529 - x += '<div style=width:100%;margin-bottom:2px;background-color:lightgray><div style=padding:4px><div style=display:inline-block;margin-right:5px><input class=DevScanCheckbox name=dp1checkbox tag="' + EscapeHtml(i) + '" type=checkbox onclick=addAmtScanToMeshCheckbox() /></div><div class=j1 style=display:inline-block></div><div style=display:inline-block;margin-left:5px;overflow-x:auto;white-space:nowrap>' + str + '</div></div></div>';
3530 - }
3531 - // If no results where found, display a nice message
3532 - if (x == '') { x = '<div style=width:100%;text-align:center;margin-top:12px>' + "Scan returned no results." + '</div><div style=width:100%;text-align:center;margin-top:12px;color:gray;line-height:1.5>' + "Sample IP range values" + '<br />192.168.0.100<br />192.168.1.0/24<br />192.167.0.1-192.168.0.100</div>'; }
3533 - }
3534 - // Set the html in the dialog box and re-enable the scan button
3535 - QH('dp1results', x);
3536 - QE('dp1range', true);
3537 - QE('dp1rangebutton', true);
3538 - QE('d2scanSelectButton', true);
3539 - break;
3540 - }
3541 - case 'notify': {
3542 - var n = { text: message.event.value, title: message.event.title, icon: message.event.icon, titleid: message.titleid, msgid: message.msgid, args: message.args };
3543 - if (message.id != null) { n.id = message.id; }
3544 - if (message.event.tag != null) { n.tag = message.event.tag; }
3545 - if (typeof message.maxtime == 'number') { n.maxtime = message.maxtime; }
3546 - addNotification(n);
3547 - break;
3548 - }
3549 - case 'traceinfo': {
3550 - if (typeof message.event.traceSources == 'object') {
3551 - if ((message.event.traceSources != null) && (message.event.traceSources.length > 0)) {
3552 - serverTraceSources = message.event.traceSources;
3553 - QH('p41traceStatus', EscapeHtml(message.event.traceSources.join(', ')));
3554 - } else {
3555 - serverTraceSources = [];
3556 - QH('p41traceStatus', "None");
3557 - }
3558 - }
3559 - break;
3560 - }
3561 - case 'sysinfohash': {
3562 - // If the sysinfo document has changed and we are looking at it, request an update.
3563 - if ((currentNode != null) && (message.event.nodeid == powerTimelineReq)) {
3564 - meshserver.send({ action: 'getsysinfo', nodeid: message.event.nodeid });
3565 - }
3566 - break;
3567 - }
3568 - case 'ifchange': {
3569 - // Network interface changed for a device, if we are currently viewing this device, ask for an update.
3570 - if ((currentNode != null) && (currentNode._id == message.event.nodeid)) { meshserver.send({ action: 'getnetworkinfo', nodeid: currentNode._id }); }
3571 - break;
3572 - }
3573 - case 'devicesessions': {
3574 - // List of sessions for a given device
3575 - var node = getNodeFromId(message.event.nodeid);
3576 - if (node == null) break; // Unknown node
3577 - node.sessions = message.event.sessions;
3578 - if (node.sessions != null) {
3579 - for (var i in node.sessions) { if (Object.keys(node.sessions[i]).length == 0) { delete node.sessions[i]; } }
3580 - if (Object.keys(node.sessions).length == 0) { delete node.sessions; }
3581 - }
3582 - updateDeviceViewDevice(node);
3583 - if ((currentNode != null) && (currentNode._id == message.event.nodeid)) { gotoDevice(currentNode._id, xxcurrentView, true); }
3584 -
3585 - // If we are looking at the sessions dialog box for this device now, update it
3586 - if (xxdialogTag == ('SESSIONS-' + message.event.nodeid)) { showDeviceSessions(message.event.nodeid, true); }
3587 - if (xxdialogTag == ('MESSAGES-' + message.event.nodeid)) { showDeviceMessages(message.event.nodeid, true); }
3588 - if (xxdialogTag == ('HELPREQ-' + message.event.nodeid)) { showDeviceHelpRequests(message.event.nodeid, true); }
3589 -
3590 - // If we are filtering on sessions or help, update the visible devices
3591 - if ((Q('DevFilterSelect').value == 2) || (Q('DevFilterSelect').value == 6)) { mainUpdate(1); }
3592 - break;
3593 - }
3594 - case 'loginTokenChanged': { // Login tokens have changed
3595 - if (message.event.userid != userinfo._id) return;
3596 - loginTokens = message.event.loginTokens;
3597 - mainUpdate(65536);
3598 - break;
3599 - }
3600 - case 'loginTokenAdded': { // A login token was added
3601 - if (message.event.userid != userinfo._id) return;
3602 - if (loginTokens == null) { loginTokens = []; }
3603 - loginTokens.push(message.event.newToken);
3604 - mainUpdate(65536);
3605 - break;
3606 - }
3607 - case 'stopped': { // Server is stopping.
3608 - // Disconnect
3609 - //console.log(message.msg);
3610 - break;
3611 - }
3612 - case 'updatePluginList': {
3613 - installedPluginList = message.event.list;
3614 - updatePluginList();
3615 - break;
3616 - }
3617 - case 'pluginStateChange': {
3618 - if (pluginHandler == null) break;
3619 - pluginHandler.refreshPluginHandler();
3620 - break;
3621 - }
3622 - case 'plugin': {
3623 - if (pluginHandler == null) break;
3624 - try { pluginHandler[message.event.plugin][message.event.pluginaction](message); } catch (e) { console.log("PluginHandler could not event message: ", e); }
3625 - break;
3626 - }
3627 - default:
3628 - //console.log('Unknown message.event.action', message.event.action);
3629 - break;
3630 - }
3631 - break;
3632 - }
3633 - case 'createInviteLink': { // Agent installation invitation link
3634 - if (xxdialogTag != message.meshid) break;
3635 - var servername = serverinfo.name;
3636 - if ((servername.indexOf('.') == -1) || ((features & 2) != 0)) { servername = window.location.hostname; } // If the server name is not set or it's in LAN-only mode, use the URL hostname as server name.
3637 - var domainUrlNoSlash = domainUrl.substring(0, domainUrl.length - 1);
3638 - var url;
3639 - if (serverinfo.https == true) {
3640 - var portStr = (serverinfo.port == 443) ? '' : (':' + serverinfo.port);
3641 - url = 'https://' + servername + portStr + domainUrl + 'agentinvite?c=' + message.cookie + (urlargs.key?('&key=' + urlargs.key):'');
3642 - } else {
3643 - var portStr = (serverinfo.port == 80) ? '' : (':' + serverinfo.port);
3644 - url = 'http://' + servername + portStr + domainUrl + 'agentinvite?c=' + message.cookie + (urlargs.key?('&key=' + urlargs.key):'');
3645 - }
3646 - Q('agentInvitationLink').href = url;
3647 - var t = format((message.expire == 1)?"1 hour":"{0} hours", message.expire);
3648 - if (message.expire == 24) { t = "1 day"; }
3649 - if (message.expire == 168) { t = "1 week"; }
3650 - if (message.expire == 5040) { t = "1 month"; }
3651 - if (message.expire == 0) { t = "Unlimited"; }
3652 - QH('agentInvitationLink', format("Invitation Link ({0})", t));
3653 - QV('agentInvitationLinkDiv', true);
3654 - break;
3655 - }
3656 - case 'createDeviceShareLink': { // Guest sharing link
3657 - if (xxdialogTag) break;
3658 - var node = getNodeFromId(message.nodeid), x = '';
3659 - if (node == null) break;
3660 - x += addHtmlValue("Device", node.name);
3661 - x += addHtmlValue("Guest Name", message.guestname);
3662 - x += addHtmlValue("User Input", message.viewOnly ? "Not allowed, view only" : "Allowed");
3663 - if (message.start && message.expire) {
3664 - if (message.recurring) {
3665 - x += addHtmlValue("Start Time", printDateTime(new Date(message.start)));
3666 - if (message.expire < 2) { x += addHtmlValue("Duration", format("{0} minute", message.expire) ); }
3667 - else { x += addHtmlValue("Duration", format("{0} minutes", message.expire) ); }
3668 - } else {
3669 - x += addHtmlValue("Start Time", printDateTime(new Date(message.start)));
3670 - x += addHtmlValue("Expire Time", printDateTime(new Date(message.expire)));
3671 - }
3672 - }
3673 - if (message.recurring == 1) { x += addHtmlValue("Recurring", "Daily"); }
3674 - if (message.recurring == 2) { x += addHtmlValue("Recurring", "Weekly"); }
3675 - var y = [];
3676 - if (message.consent & 0x0007) { y.push("Notify"); }
3677 - if (message.consent & 0x0038) { y.push("Prompt"); }
3678 - if (message.consent & 0x0040) { y.push("Privacy bar"); }
3679 - if (y.length == 0) { y.push("None"); }
3680 - x += addHtmlValue("User Consent", y.join(', '));
3681 - var type = ''; if (message.p <= 7) { type = ['', "Remote Terminal Link", "Remote Desktop Link", "Remote Desktop + Terminal Link", "Remote Files Link", "Remote Terminal + Files Link", "Remote Desktop + Files Link", "Remote Desktop + Terminal + Files Link"][message.p]; } else if (message.p == 8) { type = format("HTTP/{0} link", message.port); } else if (message.p == 16) { type = format("HTTPS/{0}", message.port); }
3682 - x += '<div id=agentInvitationLinkDiv style="text-align:center;font-size:large;margin:16px"><a href="' + message.url + '" id=agentInvitationLink rel="noreferrer noopener" target="_blank" style=cursor:pointer>' + type + '</a> <img src=images/link4.png height=10 width=10 title="' + "Copy link to clipboard" + '" style=cursor:pointer onclick=d2CopyInviteToClip()></div></div>';
3683 - setDialogMode(2, "Share Device", 1, null, x);
3684 - break;
3685 - }
3686 - case 'getmqttlogin': {
3687 - if ((currentNode == null) || (currentNode._id != message.nodeid) || (xxdialogMode != null)) return;
3688 - var x = "These settings can be used to connect MQTT for this device." + '<br /><br />';
3689 - delete message.action;
3690 - delete message.nodeid;
3691 - x += '<textarea readonly=readonly style=width:100%;resize:none;height:100px;overflow:auto;font-size:12px readonly>' + JSON.stringify(message) + '</textarea>';
3692 - /*
3693 - x += addHtmlValue('Username', '<input style=width:230px readonly value="' + message.user + '" />');
3694 - x += addHtmlValue('Password', '<input style=width:230px readonly value="' + message.pass + '" />');
3695 - x += addHtmlValue('WS URL', '<input style=width:230px readonly value="' + message.wsUrl + '" />');
3696 - if (message.mpsUrl && message.mpsCertHash) {
3697 - x += addHtmlValue('MPS URL', '<input style=width:230px readonly value="' + message.mpsUrl + '" />');
3698 - x += addHtmlValue('MPS Cert Hash', '<input style=width:230px readonly value="' + message.mpsCertHash + '" />');
3699 - }
3700 - */
3701 - setDialogMode(2, "MQTT Credentials", 1, null, x);
3702 - break;
3703 - }
3704 - case 'stopped': { // Server is stopping.
3705 - // Disconnect
3706 - autoReconnect = false;
3707 - QH('p0span', message.msg);
3708 - break;
3709 - }
3710 - case 'updatePluginList': {
3711 - installedPluginList = message.list;
3712 - updatePluginList();
3713 - break;
3714 - }
3715 - case 'pluginVersionsAvailable': {
3716 - if (pluginHandler == null) break;
3717 - updatePluginList(message.list);
3718 - break;
3719 - }
3720 - case 'downgradePluginVersions': {
3721 - var vSelect = '<select id="lastPluginVersion">';
3722 - message.info.versionList.forEach(function(v) { vSelect += '<option value="' + v.zipball_url + '">' + v.name + '</option>'; });
3723 - vSelect += '</select>';
3724 - setDialogMode(2, "Plugin Action", 3, pluginActionEx, format('Select the version to downgrade the plugin: {0}', message.info.name) + '<hr />' + vSelect + '<hr />' + "Please be aware that downgrading is not recommended. Please only do so in the event that a recent upgrade has broken something." + + '<input id="lastPluginAct" type="hidden" value="downgrade" /><input id="lastPluginId" type="hidden" value="' + message.info.id + '" />');
3725 - break;
3726 - }
3727 - case 'serverBackup': {
3728 - if (message.service == 'googleDrive') {
3729 - QV('p2ServerActionsGoogleBackup', message.state > 0);
3730 - QV('p2ServerActionsGoogleBackupCheck', message.state > 2);
3731 - miscState['googleDrive'] = { state: message.state }
3732 - if (message.state == 2) {
3733 - var x = '<img style=float:right src="images/googledrive-48.png" /><div>' + "Nagivate to the URL below, grant access and copy the token code back." + '</div><br />';
3734 - x += addHtmlValue("Activation", '<a href=\"' + message.url + '\" rel="noreferrer noopener" target="_blank">Browse to this URL</a>');
3735 - x += addHtmlValue("Code", '<input id=gdcode onkeyup=server_setupGoogleDriveBackupCheck3() style=width:240px></input>');
3736 - setDialogMode(2, "Google Drive Backup", 3, server_setupGoogleDriveBackupEx, x, 'gd2');
3737 - Q('gdcode').focus();
3738 - QE('idx_dlgOkButton', false);
3739 - }
3740 - }
3741 - break;
3742 - }
3743 - case 'pluginError': {
3744 - setDialogMode(2, "Plugin Error", 1, null, message.msg);
3745 - break;
3746 - }
3747 - case 'plugin': {
3748 - if ((pluginHandler == null) || (typeof message.plugin != 'string')) break;
3749 - try { pluginHandler[message.plugin][message.method](server, message); } catch (e) { console.log('Error loading plugin handler ('+ e + ')'); }
3750 - break;
3751 - }
3752 - case 'amtsetupbin': {
3753 - saveAs(new Blob([ Uint8Array.from(atob(message.file), function (c) { return c.charCodeAt(0) }) ], { type: 'application/octet-stream' }), 'setup.bin');
3754 - break;
3755 - }
3756 - case 'previousLogins':{
3757 - var tag = 'previousLogins';
3758 - if (message.userid != null) { tag += ':' + message.userid; }
3759 - if ((xxdialogMode == 2) && (xxdialogTag == tag)) {
3760 - var x = '', c = 'BBB', xx = '';
3761 - if (message.events.length == 0) {
3762 - x += 'No previous login.';
3763 - } else {
3764 - x += '<div style=max-height:260px;overflow-y:scroll;overflow-x:hidden><table>';
3765 - for (var i in message.events) {
3766 - var m = message.events[i].m;
3767 - if (m == 107) { m = "Valid login"; c = 'BBD1BB'; xx = ''; if (message.events[i].tn != null) { m = format("Token: {0}", message.events[i].tn); c = '88D188' } }
3768 - else if (m == 108) { m = "Invalid 2FA"; c ='DD9DC3'; xx = 'x'; }
3769 - else if (m == 109) { m = "Locked account"; c ='E1BBBB'; xx = 'x'; }
3770 - else if (m == 110) { m = "Invalid password"; c = 'E1BBBB'; xx = 'x'; }
3771 - x += '<tr><td><img src=images/user-32' + xx + '.png height=32 width=32 style=float:left srcset="images/user-64' + xx + '.png 2x"><td><div style=width:300px;background-color:#' + c + ';border-radius:6px;margin-bottom:4px;padding:4px><div>' + printDateTime(new Date(message.events[i].t)) + ', <b>' + EscapeHtml(m) + '</b></div><div style=font-size:x-small>' + EscapeHtml(message.events[i].a.join(', ')) + '</div></div></tr>';
3772 - }
3773 - x += '</table></div>';
3774 - }
3775 - setDialogMode(2, "Previous Logins", 1, null, x);
3776 - }
3777 - break;
3778 - }
3779 - case 'getDeviceDetails': {
3780 - saveAs(new Blob([message.data], { type: 'application/octet-stream' }), "devicelist" + '.' + message.type);
3781 - break;
3782 - }
3783 - case 'createLoginToken': { // A new login token was created
3784 - if (xxdialogMode) return;
3785 - var x = "Take note of this username and password, the password cannot be shown again." + '<br /><br />';
3786 - x += addHtmlValue("Name", EscapeHtml(message.name))
3787 - if (message.expire != 0) { x += addHtmlValue("Expire", EscapeHtml(printDateTime(new Date(message.expire)))); }
3788 - x += addHtmlValue("Username", '<img src="images/link4.png" title="' + "Copy link to clipboard" + '" style="margin:9px;cursor:pointer;float:right" onclick=copyTextToClip2("' + encodeURIComponentEx(message.tokenUser) + '") width=10 height=10><div class=selecttext style=width:230px;padding:5px;background-color:orange;border-radius:5px;font-size:15px>' + EscapeHtml(message.tokenUser) + '</div>');
3789 - x += addHtmlValue("Password", '<img src="images/link4.png" title="' + "Copy link to clipboard" + '" style="margin:9px;cursor:pointer;float:right" onclick=copyTextToClip2("' + encodeURIComponentEx(message.tokenPass) + '") width=10 height=10><div class=selecttext style=width:230px;padding:5px;background-color:orange;border-radius:5px;font-size:15px>' + EscapeHtml(message.tokenPass) + '</div>');
3790 - setDialogMode(2, "Create Login Token", 1, null, x);
3791 - break;
3792 - }
3793 - case 'loginTokens': { // Reveiced the list of login tokens
3794 - loginTokens = message.loginTokens;
3795 - mainUpdate(65536);
3796 - break;
3797 - }
3798 - case 'report': {
3799 - renderReport(message.data);
3800 - break;
3801 - }
3802 - default:
3803 - //console.log('Unknown message.action', message.action);
3804 - break;
3805 - }
3806 - }
3807 -
3808 - // Go to the correct starting view page
3809 - function gotoStartViewPage() {
3810 - var xviewmode = parseInt('{{viewmode}}');
3811 - if (xxcurrentView != -1) return;
3812 - if ('{{currentNode}}'.toLowerCase() != '') { // The .toLowerCase here is the minifier will not optimize this out.
3813 - if (getNodeFromId('{{currentNode}}') == null) return; // This node is not loaded yet
3814 - gotoDevice('{{currentNode}}', xviewmode);
3815 - } else if (args.gotonode != null) {
3816 - if (args.gotonode.length == 96) { args.gotonode = btoa(hex2rstr(args.gotonode)).split('+').join('@').split('/').join('$'); } // This is a HEX encoded NodeID, convert it to Base64
3817 - if (getNodeFromId('node/' + domain + '/' + args.gotonode) == null) return; // This node is not loaded yet
3818 - gotoDevice('node/' + domain + '/' + args.gotonode, xviewmode);
3819 - goBackStack.push(1);
3820 - } else if (args.gotodevicename != null) {
3821 - var foundNode = null;
3822 - if (nodes != null) { for (var i in nodes) { if (nodes[i].name == args.gotodevicename) { foundNode = nodes[i]._id; } } }
3823 - if (foundNode) { gotoDevice(foundNode, xviewmode); goBackStack.push(1); }
3824 - } else if (args.gotomesh != null) {
3825 - if (meshes['mesh/' + domain + '/' + args.gotomesh] == null) return; // This device group is not loaded yet
3826 - gotoMesh('mesh/' + domain + '/' + args.gotomesh);
3827 - go(xviewmode);
3828 - goBackStack.push(2);
3829 - } else if (args.gotouser != null) {
3830 - var xuserid = args.gotouser;
3831 - if (args.gotouser.indexOf('/') < 0) { xuserid = 'user/' + domain + '/' + args.gotouser; }
3832 - if ((users == null) || (users[xuserid] == null)) return; // This user is not loaded yet
3833 - gotoUser(encodeURIComponentEx(xuserid));
3834 - go(xviewmode);
3835 - goBackStack.push(4);
3836 - } else if (args.gotougrp != null) {
3837 - var xusergrpid = args.gotougrp;
3838 - if (args.gotougrp.indexOf('/') < 0) { xusergrpid = 'ugrp/' + domain + '/' + args.gotougrp; }
3839 - if ((usergroups == null) || usergroups[xusergrpid] == null) return; // This user group is not loaded yet
3840 - gotoUserGroup(xusergrpid);
3841 - go(xviewmode);
3842 - goBackStack.push(50);
3843 - } else if (!isNaN(xviewmode)) {
3844 - go(xviewmode);
3845 - } else {
3846 - setDialogMode(0);
3847 - go(1);
3848 - }
3849 - delete args.gotonode;
3850 - delete args.gotomesh;
3851 - delete args.gotouser;
3852 - delete args.gotougrp;
3853 - }
3854 -
3855 - //
3856 - // MY DEVICES
3857 - //
3858 -
3859 - function onRealNameCheckBox() {
3860 - showRealNames = Q('RealNameCheckBox').checked;
3861 - putstore('showRealNames', showRealNames ? 1 : 0);
3862 - mainUpdate(7);
3863 - }
3864 -
3865 - function onOnlineCheckBox(e) {
3866 - putstore('devFilterSelect', Q('DevFilterSelect').value);
3867 - onDeviceSearchChanged(e);
3868 - }
3869 -
3870 - function updateDevicePageState() {
3871 - if ((devicePagingState == null) || (devicePagingState.total <= devicePagingState.limit)) {
3872 - QV('devViewPageState', false);
3873 - QV('devViewPageButton1', false);
3874 - QV('devViewPageButton2', false);
3875 - QV('devViewPageButton3', false);
3876 - QV('devViewPageButton4', false);
3877 - } else {
3878 - var currentPage = Math.floor((devicePagingState.skip + devicePagingState.limit) / devicePagingState.limit);
3879 - var maxPage = Math.ceil(devicePagingState.total / devicePagingState.limit);
3880 - QV('devViewPageState', true);
3881 - QV('devViewPageButton1', true);
3882 - QV('devViewPageButton2', true);
3883 - QV('devViewPageButton3', true);
3884 - QV('devViewPageButton4', true);
3885 - QH('devViewPageState', currentPage + '/' + maxPage);
3886 - }
3887 - }
3888 -
3889 - function onDeviceViewPageChange(i) {
3890 - if (devicePagingState == null) return;
3891 - var currentPage = (Math.floor((devicePagingState.skip + devicePagingState.limit) / devicePagingState.limit));
3892 - var maxPage = Math.ceil(devicePagingState.total / devicePagingState.limit);
3893 - switch (i) {
3894 - case 1: { if (currentPage > 1) meshserver.send({ action: 'nodes', skip: 0 }); break; } // Goto first page
3895 - case 2: { if (currentPage > 1) meshserver.send({ action: 'nodes', skip: (currentPage - 2) * devicePagingState.limit }); break; } // Goto previous page
3896 - case 3: { if (currentPage < maxPage) meshserver.send({ action: 'nodes', skip: currentPage * devicePagingState.limit }); break; } // Goto next page
3897 - case 4: { if (currentPage < maxPage) meshserver.send({ action: 'nodes', skip: (maxPage - 1) * devicePagingState.limit }); break; } // Goto last page
3898 - }
3899 - }
3900 -
3901 - function onDeviceViewChange(i) {
3902 - if (i != null) { Q('viewselect').value = i; }
3903 - for (var j = 1; j < 6; j++) { Q('devViewButton' + j).classList.remove('viewSelectorSel'); }
3904 - Q('devViewButton' + Q('viewselect').value).classList.add('viewSelectorSel');
3905 - putstore('deviceView', Q('viewselect').value);
3906 - putstore('viewsize', Q('sizeselect').value);
3907 - mainUpdate(4);
3908 - setTimeout(function () { mainUpdate(512); }, 200);
3909 - }
3910 -
3911 - function onDeviceViewSettings() {
3912 - if (xxdialogMode) return;
3913 -
3914 - // Use defaults if needed
3915 - if (deviceViewSettings == null) { deviceViewSettings = {}; }
3916 - if (!Array.isArray(deviceViewSettings.devsCols)) { deviceViewSettings.devsCols = ['user','ip','conn']; }
3917 -
3918 - // Display the dialog box
3919 - var x = '';
3920 - x += '<label><input id=d2c8 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('agtype') >= 0)?' checked':'') + '>' + "Agent Type" + '</label><br />';
3921 - x += '<label><input id=d2c9 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('agver') >= 0)?' checked':'') + '>' + "Agent Version" + '</label><br />';
3922 - x += '<label><input id=d2c6 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('desc') >= 0)?' checked':'') + '>' + "Device Description" + '</label><br />';
3923 - x += '<label><input id=d2c5 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('os') >= 0)?' checked':'') + '>' + "Operating System" + '</label><br />';
3924 - x += '<label><input id=d2c1 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('links') >= 0)?' checked':'') + '>' + "MeshCentral Router Links" + '</label><br />';
3925 - x += '<label><input id=d2c2 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('user') >= 0)?' checked':'') + '>' + "Logged in users" + '</label><br />';
3926 - x += '<label><input id=d2c3 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('ip') >= 0)?' checked':'') + '>' + "Agent IP address" + '</label><br />';
3927 - x += '<label><input id=d2c4 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('conn') >= 0)?' checked':'') + '>' + "Server Connectivity" + '</label><br />';
3928 - x += '<label><input id=d2c7 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('lastseen') >= 0)?' checked':'') + '>' + "Last Seen" + '</label><br />';
3929 - setDialogMode(2, "Device View Columns", 3, onDeviceViewSettingsEx, x);
3930 - }
3931 -
3932 - function onDeviceViewSettingsEx() {
3933 - var cols = [];
3934 - if (Q('d2c1').checked) { cols.push('links'); }
3935 - if (Q('d2c2').checked) { cols.push('user'); }
3936 - if (Q('d2c3').checked) { cols.push('ip'); }
3937 - if (Q('d2c4').checked) { cols.push('conn'); }
3938 - if (Q('d2c5').checked) { cols.push('os'); }
3939 - if (Q('d2c6').checked) { cols.push('desc'); }
3940 - if (Q('d2c7').checked) { cols.push('lastseen'); }
3941 - if (Q('d2c8').checked) { cols.push('agtype'); }
3942 - if (Q('d2c9').checked) { cols.push('agver'); }
3943 - deviceViewSettings.devsCols = cols;
3944 - putstore('_deviceViewSettings', JSON.stringify(deviceViewSettings));
3945 - mainUpdate(4);
3946 - }
3947 -
3948 - function ondockeypress(e) {
3949 - setSessionActivity();
3950 - if (!xxdialogMode && (xxcurrentView == 11) && desktop && Q('DeskControl').checked) {
3951 - // Check what keys we are allows to send
3952 - if (currentNode != null) {
3953 - var meshrights = GetNodeRights(currentNode);
3954 - var inputAllowed = ((features2 & 0x2000) == 0) && ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
3955 - if (inputAllowed == false) return false;
3956 - var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
3957 - if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; }
3958 - }
3959 - return desktop.m.handleKeys(e);
3960 - }
3961 - if (!xxdialogMode && (xxcurrentView == 12) && terminal && (terminal.State == 3) && (xterm == null)) { return terminal.m.TermHandleKeys(e); }
3962 - if (!xxdialogMode && ((xxcurrentView == 15) || (xxcurrentView == 115))) return agentConsoleHandleKeys(e);
3963 - if (!xxdialogMode && xxcurrentView == 4) {
3964 - if ((e.ctrlKey == true) || (e.altKey == true) || (e.metaKey == true)) return;
3965 - var processed = 0;
3966 - if (e.key) {
3967 - if ((e.key.length === 1) && (userSearchFocus == 0)) { Q('UserSearchInput').value = ((Q('UserSearchInput').value + e.key)); processed = 1; }
3968 - if ((e.keyCode == 8) && (userSearchFocus == 0)) { var x = Q('UserSearchInput').value; Q('UserSearchInput').value = x.substring(0, x.length - 1); processed = 1; }
3969 - if (e.keyCode == 27) { Q('UserSearchInput').value = ''; processed = 1; }
3970 - } else {
3971 - if (e.charCode != 0 && userSearchFocus == 0) { Q('UserSearchInput').value = ((Q('UserSearchInput').value + String.fromCharCode(e.charCode))); processed = 1; }
3972 - }
3973 - if (processed > 0) { if (processed == 1) { onUserSearchInputChanged(); } return haltEvent(e); }
3974 - }
3975 - if (xxdialogMode || xxcurrentView != 1) return;
3976 - if (e.ctrlKey == true && e.charCode == 96) {
3977 - showRealNames = !showRealNames;
3978 - Q('RealNameCheckBox').value = showRealNames;
3979 - putstore('showRealNames', showRealNames ? 1 : 0);
3980 - mainUpdate(6)
3981 - return;
3982 - }
3983 - if (e.ctrlKey == true || e.altKey == true || e.metaKey == true) return;
3984 - if (Q('viewselect').value < 4) {
3985 - var processed = 0;
3986 - if (e.key) {
3987 - if ((e.key.length === 1) && (searchFocus == 0)) { Q('KvmSearchInput').value = Q('SearchInput').value = ((Q('SearchInput').value + e.key)); processed = 1; }
3988 - if ((e.keyCode == 8) && (searchFocus == 0)) { var x = Q('SearchInput').value; Q('KvmSearchInput').value = Q('SearchInput').value = x.substring(0, x.length - 1); processed = 1; }
3989 - if (e.keyCode == 27) { Q('KvmSearchInput').value = Q('SearchInput').value = ''; processed = 1; }
3990 - } else {
3991 - if (e.charCode != 0 && searchFocus == 0) { Q('KvmSearchInput').value = Q('SearchInput').value = ((Q('SearchInput').value + String.fromCharCode(e.charCode))); processed = 1; }
3992 - }
3993 - if (processed > 0) { if (processed == 1) { mainUpdate(5); } return haltEvent(e); }
3994 - }
3995 - if (Q('viewselect').value == 4) {
3996 - if (e.key) {
3997 - if ((e.key.length === 1) && (mapSearchFocus == 0)) { Q('mapSearchLocation').value = ((Q('mapSearchLocation').value + e.key)); processed = 1; }
3998 - //if (e.keyCode == 8 && mapSearchFocus == 0) { var x = Q('mapSearchLocation').value; Q('mapSearchLocation').value = x.substring(0, x.length - 1); processed = 1; }
3999 - if (e.keyCode == 27) { Q('mapSearchLocation').value = ''; mapCloseSearchWindow(); processed = 1; }
4000 - if (e.keyCode == 13) { getSearchLocation(); }
4001 - } else {
4002 - if (e.charCode != 0 && mapSearchFocus == 0) { Q('mapSearchLocation').value = ((Q('mapSearchLocation').value + String.fromCharCode(e.charCode))); processed = 1; }
4003 - }
4004 - }
4005 - }
4006 -
4007 - function ondockeydown(e) {
4008 - setSessionActivity();
4009 - if (!xxdialogMode && (xxcurrentView == 11) && desktop && Q('DeskControl').checked) {
4010 - // Check what keys we are allows to send
4011 - if (currentNode != null) {
4012 - var meshrights = GetNodeRights(currentNode);
4013 - var inputAllowed = ((features2 & 0x2000) == 0) && ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
4014 - if (inputAllowed == false) return false;
4015 - var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
4016 - if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; }
4017 - }
4018 - return desktop.m.handleKeyDown(e);
4019 - }
4020 - if (!xxdialogMode && (xxcurrentView == 12) && terminal && (terminal.State == 3) && xterm == null) { terminal.m.TermHandleKeyDown(e); if ((e.keyCode >= 37) && (e.keyCode <= 40)) { haltEvent(e); } }
4021 - if (!xxdialogMode && (xxcurrentView == 13) && (e.keyCode == 116) && (p13filetree != null)) { haltEvent(e); return false; } // F5 Refresh on files
4022 - if (!xxdialogMode && ((xxcurrentView == 15) || (xxcurrentView == 115))) { return agentConsoleHandleKeys(e); }
4023 - if (!xxdialogMode && (xxcurrentView == 4)) {
4024 - if ((e.keyCode === 8) && (userSearchFocus == 0)) { var x = Q('UserSearchInput').value; Q('UserSearchInput').value = (x.substring(0, x.length - 1)); processed = 1; }
4025 - if (e.keyCode === 27) { Q('UserSearchInput').value = ''; processed = 1; }
4026 - if (processed > 0) { if (processed == 1) { mainUpdate(5); } return haltEvent(e); }
4027 - }
4028 - if (xxdialogMode || (xxcurrentView != 1) || (e.ctrlKey == true) || (e.altKey == true) || (e.metaKey == true)) return;
4029 - var processed = 0;
4030 - if (Q('viewselect').value < 4) {
4031 - if ((e.keyCode === 8) && (searchFocus == 0)) { var x = Q('SearchInput').value; Q('KvmSearchInput').value = Q('SearchInput').value = (x.substring(0, x.length - 1)); processed = 1; }
4032 - if (e.keyCode === 27) { Q('KvmSearchInput').value = Q('SearchInput').value = ''; processed = 1; }
4033 - if (processed > 0) { if (processed == 1) { mainUpdate(5); } return haltEvent(e); }
4034 - }
4035 - if (Q('viewselect').value == 4) {
4036 - if ((e.keyCode === 8) && (mapSearchFocus == 0)) { var x = Q('mapSearchLocation').value; Q('mapSearchLocation').value = (x.substring(0, x.length - 1)); processed = 1; }
4037 - if (e.keyCode === 27) { Q('mapSearchLocation').value = ''; mapCloseSearchWindow(); processed = 1; }
4038 - }
4039 - }
4040 -
4041 - function ondockeyup(e) {
4042 - setSessionActivity();
4043 - if (!xxdialogMode && (xxcurrentView == 11) && desktop && Q('DeskControl').checked) {
4044 - // Check what keys we are allows to send
4045 - if (currentNode != null) {
4046 - var meshrights = GetNodeRights(currentNode);
4047 - var inputAllowed = ((features2 & 0x2000) == 0) && ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
4048 - if (inputAllowed == false) return false;
4049 - var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
4050 - if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; }
4051 - }
4052 - return desktop.m.handleKeyUp(e);
4053 - }
4054 - if (!xxdialogMode && (xxcurrentView == 12) && terminal && (terminal.State == 3) && xterm == null) { return terminal.m.TermHandleKeyUp(e); }
4055 - if (!xxdialogMode && (xxcurrentView == 13) && (e.keyCode == 116) && (p13filetree != null)) { p13folderup(9999); haltEvent(e); return false; } // F5 Refresh on files
4056 - if (!xxdialogMode && (xxcurrentView == 4)) { if (((e.keyCode === 8) && (searchFocus == 0)) || e.keyCode === 27) { return haltEvent(e); } }
4057 - if (xxdialogMode && (e.keyCode == 27)) { dialogclose(0); }
4058 - if ((e.shiftKey == true) && (e.keyCode == 27)) { dialogclose(0); Q('KvmSearchInput').value = Q('SearchInput').value = ''; mainUpdate(5); if (desktop != null) { connectDesktop(); } if (terminal != null) { connectTerminal(); } if (files != null) { connectFiles(); } go(1); return; } // Shift-ESC: Reset the web page
4059 - if (xxdialogMode || xxcurrentView != 0 || e.ctrlKey == true || (e.altKey == true) || (e.metaKey == true)) return;
4060 - if (Q('viewselect').value < 4) { if (((e.keyCode === 8) && (searchFocus == 0)) || (e.keyCode === 27)) { return haltEvent(e); } }
4061 - if (Q('viewselect').value == 4) { if (((e.keyCode === 8) && (mapSearchFocus == 0)) || (e.keyCode === 27)) { return haltEvent(e); } }
4062 - }
4063 -
4064 - //function ondocfocus() { }
4065 - // TODO: Add handleReleaseKeys() for Intel AMT.
4066 - function ondocblur() { if (!xxdialogMode && xxcurrentView == 11 && desktop && Q('DeskControl').checked && desktop.m.handleReleaseKeys) { return desktop.m.handleReleaseKeys(); } }
4067 -
4068 - // Highlights the device group hovered
4069 - function devGrpMouseHover(element, over) {
4070 - setSessionActivity();
4071 - var view = Q('viewselect').value;
4072 - var e = element.children[1].children[1];
4073 - e.children[0].classList.remove('g1s');
4074 - e.children[1].classList.remove('e2s');
4075 - e.children[2].classList.remove('g2s');
4076 - if (over == 1) {
4077 - e.children[0].classList.add('g1s');
4078 - e.children[1].classList.add('e2s');
4079 - e.children[2].classList.add('g2s');
4080 - }
4081 - }
4082 -
4083 - // Highlights the device being hovered
4084 - function devMouseHover(element, over) {
4085 - setSessionActivity();
4086 - var view = Q('viewselect').value;
4087 - if (view == 1) {
4088 - var e = element.children[0].children[0].children[1].children[0].children[0].children[0];
4089 - e.children[1].classList.remove('g1s');
4090 - e.children[2].classList.remove('e2s');
4091 - e.children[3].classList.remove('g2s');
4092 - if (over == 1) {
4093 - e.children[1].classList.add('g1s');
4094 - e.children[2].classList.add('e2s');
4095 - e.children[3].classList.add('g2s');
4096 - }
4097 - } else if (view == 2) {
4098 - var e = element;
4099 - e.children[2].classList.remove('g1s');
4100 - e.children[4].classList.remove('e2s');
4101 - e.children[3].classList.remove('g2s');
4102 - if (over == 1) {
4103 - e.children[2].classList.add('g1s');
4104 - e.children[4].classList.add('e2s');
4105 - e.children[3].classList.add('g2s');
4106 - }
4107 - }
4108 - }
4109 -
4110 - var deviceHeaderId = 0;
4111 - var deviceHeaderTotal = 0;
4112 - var deviceHeadersTitles = {};
4113 - var deviceHeaderCount;
4114 - var deviceHeaders = {};
4115 - var oldviewmode = 0;
4116 - function updateDevices() {
4117 - if ((nodes == null) || (xxcurrentView != 1)) { return; }
4118 - var r = '', c = 0, current = null, count = 0, scount = 0, displayedMeshes = {}, view = Q('viewselect').value, groups = {}, groupCount = {};
4119 - QV('xdevices', view != 4);
4120 - QV('xdevicesmap', view == 4);
4121 - QV('devListToolbar', view < 3);
4122 - QV('kvmListToolbar', (view == 3) || (view == 5));
4123 - QV('devMapToolbar', view == 4);
4124 - QV('devListToolbarSize', (view == 3) || (view == 5));
4125 - QV('devListToolbarSettings', view == 2);
4126 - QV('NoMeshesPanel', (nodes.length == 0) && (meshcount == 0));
4127 - //QV('devListToolbarView', (meshcount != 0) && (nodes.length > 0));
4128 - QV('devListToolbarViewIcons', nodes.length > 0);
4129 - QV('devListToolbarSort', (nodes.length > 0) && (view != 4));
4130 - if (nodes.length == 0) { view = 1; sort = 0; }
4131 - if (view == 4) {
4132 - setTimeout( function() { if (xxmap.map != null) { xxmap.map.updateSize(); } }, 200);
4133 - // TODO
4134 - } else {
4135 - // 3 wide, list view or desktop view
4136 - deviceHeaderId = 0;
4137 - deviceHeaderCount = {};
4138 - deviceHeaderTotal = 0;
4139 - deviceHeaders = {};
4140 - deviceHeadersTitles = {};
4141 - var kvmDivs = [];
4142 -
4143 - // Perform node sort
4144 - if (sort == 0) { nodes.sort(meshSort); }
4145 - else if (sort == 1) { nodes.sort(powerSort); }
4146 - else if (sort == 2) { if (showRealNames == true) { nodes.sort(deviceHostSort); } else { nodes.sort(deviceSort); } }
4147 - else if (sort == 5) {
4148 - // If the last seen column is not turned on, turn it on first (we require this to sort the data)
4149 - if (!(deviceViewSettings && deviceViewSettings.devsCols && deviceViewSettings.devsCols.indexOf('lastseen') >= 0)) {
4150 - // Request last connection data if not requested yet
4151 - if (requestedLastConnects == false) { requestedLastConnects = true; meshserver.send({ action: 'lastconnects' }); }
4152 - // Force initialize the view settings
4153 - if (deviceViewSettings == null) { deviceViewSettings = {}; }
4154 - if (!Array.isArray(deviceViewSettings.devsCols)) { deviceViewSettings.devsCols = ['user','ip','conn', 'lastseen']; }
4155 - else { deviceViewSettings.devsCols.push('lastseen'); }
4156 - }
4157 - nodes.sort(lastConnectSort);
4158 - }
4159 -
4160 - // Compute the width of the device view.
4161 - var totalDeviceViewWidth = Q('column_l').clientWidth - 60;
4162 - var deviceBoxWidth = Math.floor(totalDeviceViewWidth / 301);
4163 - deviceBoxWidth = 301 + Math.floor((totalDeviceViewWidth - (deviceBoxWidth * 301)) / deviceBoxWidth);
4164 -
4165 - // Compute number of colums
4166 - var colcount = 4;
4167 - if (deviceViewSettings && deviceViewSettings.devsCols) {
4168 - colcount = deviceViewSettings.devsCols.length + 1;
4169 - if (deviceViewSettings.devsCols.indexOf('desc') >= 0) { colcount--; } // Description is not an extra column.
4170 - }
4171 -
4172 - // Go thru the list of nodes and display them
4173 - for (var i in nodes) {
4174 - var node = nodes[i];
4175 - if (node.v == false) continue;
4176 - var mesh2 = meshes[node.meshid];
4177 - //if ((view == 3) && (mesh2.mtype == 1)) continue;
4178 - var meshrights = GetNodeRights(node);
4179 - if (sort == 0) {
4180 - // Mesh header
4181 - if (((meshes[node.meshid]?node.meshid:'*') != current)) {
4182 - if (((view == 1) || (view == 3) || (view == 5)) && (current != null)) { r += '</div>'; } // Close collapse div
4183 - deviceHeaderSet();
4184 - var extra = '';
4185 - if (view == 2) { r += '<tr><td colspan=' + colcount + '>'; }
4186 - if (meshes[node.meshid] && (meshes[node.meshid].mtype == 1)) { extra = '<span class=devHeaderx>' + ", Intel&reg; AMT only" + '</span>'; }
4187 - if (meshes[node.meshid] && (meshes[node.meshid].mtype == 3)) { if (meshes[node.meshid].relayid) { extra = '<span class=devHeaderx>' + ", Relayed Devices" + '</span>'; } else { extra = '<span class=devHeaderx>' + ", Local Devices" + '</span>'; } }
4188 - if ((view == 1) && (current != null)) { if (c == 2) { r += '<td><div style=width:301px></div></td>'; } if (r != '') { r += '</tr></table>'; } }
4189 - if (view == 2) { r += '<div>'; }
4190 - r += '<div class=DevSt style=width:100%;padding-top:4px><span style=float:right>';
4191 - r += '<span id=DevxHeader' + deviceHeaderId + ' class=devHeaderx></span>' + extra;
4192 - r += '</span>';
4193 - if ((view == 1) || (view == 2) || (view == 3) || (view == 5)) {
4194 - var collapsed = CollapsedGroups[node.meshid];
4195 - r += '<img class=collapseImage cmenu=expandAllContextMenu id="DevxColImg' + deviceHeaderId + '" src=images/c' + ((collapsed === true)?'1':'2') + '.png height=8 width=8 style=margin-left:2px;margin-right:2px;cursor:pointer onclick=toggleCollapseGroup("' + deviceHeaderId + '","' + node.meshid + '",' + view + ')></img>'; // Collapse action
4196 - }
4197 - if (meshes[node.meshid]) {
4198 - r += '<span id=MxMESH cmenu=meshContextMenu tabindex=0 style=cursor:pointer onclick=gotoMesh("' + node.meshid + '") onkeypress="if (event.key==\'Enter\') gotoMesh(\'' + node.meshid + '\')">' + EscapeHtml(meshes[node.meshid].name) + '</span>' + getMeshActions(mesh2, meshrights) + '</div>';
4199 - current = node.meshid;
4200 - } else {
4201 - r += '<span id=MxMESH><i>' + "Individual Devices" + '</i></span></div>';
4202 - current = '*';
4203 - }
4204 - if (view == 2) { r += '</div>'; }
4205 - displayedMeshes[current] = 1;
4206 - c = 0;
4207 - if ((view == 1) || (view == 3) || (view == 5)) { r += '<div id=DevxCol' + deviceHeaderId + ((collapsed === true)?' style=display:none':'') + '>'; } // Open collapse div
4208 - }
4209 - } else if (sort == 1) {
4210 - // Power header
4211 - var pwr = node.pwr?node.pwr:0;
4212 - if (pwr !== current) {
4213 - if (((view == 1) || (view == 3) || (view == 5)) && (current != null)) { r += '</div>'; } // Close collapse div
4214 - deviceHeaderSet();
4215 - if ((view == 1) && (current !== null)) { if (c == 2) { r += '<td><div style=width:301px></div></td>'; } if (r != '') { r += '</tr></table>'; } }
4216 -
4217 - if (view == 2) { r += '<tr><td colspan=' + colcount + '>'; }
4218 - r += '<div class=DevSt style=width:100%;padding-top:4px><span id=DevxHeader' + deviceHeaderId + ' class=devHeaderx style=float:right></span>';
4219 - if ((view == 1) || (view == 2) || (view == 3) || (view == 5)) {
4220 - var collapsed = CollapsedGroups['pwr:' + pwr];
4221 - r += '<img class=collapseImage cmenu=expandAllContextMenu id="DevxColImg' + deviceHeaderId + '" src=images/c' + ((collapsed === true)?'1':'2') + '.png height=8 width=8 style=margin-left:2px;margin-right:2px;cursor:pointer onclick=toggleCollapseGroup("' + deviceHeaderId + '","pwr:' + pwr + '",' + view + ')></img>'; // Collapse action
4222 - }
4223 - r += '<span>' + PowerStateStr2(node.pwr) + '</span></div>';
4224 -
4225 - current = pwr;
4226 - c = 0;
4227 - if ((view == 1) || (view == 3) || (view == 5)) { r += '<div id=DevxCol' + deviceHeaderId + ((collapsed === true)?' style=display:none':'') + '>'; } // Open collapse div
4228 - }
4229 - } else if (sort == 2 || sort == 5) {
4230 - // Device header
4231 - if (current == null) { current = '1'; }
4232 - }
4233 -
4234 - count++;
4235 - if (view == 1) {
4236 - // Draw the device standin
4237 - r += '<div name=xxdevice1 id=xv1' + node._id + ' style=display:inline-block;position:relative;width:' + deviceBoxWidth + 'px;height:50px;padding-top:1px;padding-bottom:1px></div>';
4238 - } else if (view == 2) {
4239 - // Draw the device standin
4240 - var collapseName = node.meshid;
4241 - if (sort == 1) { collapseName = ('pwr:' + (node.pwr?node.pwr:0)); }
4242 - else if ((sort == 3) || (sort == 4)) { collapseName = 'tag:**xx**xx*TaG*xx**xx**'; }
4243 - var collapsed = (sort != 3) & (sort != 4) & CollapsedGroups[collapseName];
4244 - r += '<tr name=xxdevice2 colname=DevxCol' + collapseName + ' style=height:20px' + (collapsed?';display:none':'') + ' id=xv2' + node._id + '></tr>';
4245 - } else if (((view == 3) || (view == 5)) && (node.conn & 1) && (((meshrights & 8) || (meshrights & 256)) != 0) && (node.agent) && ((node.agent.caps & 1) != 0)) { // Check if we have rights and agent is capable of KVM.
4246 - // Draw the device (TODO: See if we can replace this with a standin in the future)
4247 - if ((Object.keys(checkedNodeids).length == 0) || checkedNodeids[node._id]) {
4248 - r += updateDeviceViewHtml(view, null, node);
4249 - kvmDivs.push(node._id);
4250 - }
4251 - }
4252 -
4253 - // If we are displaying devices by tags, put the device in the right tag group.
4254 - if (((sort == 3) || (sort == 4)) && (r != '')) {
4255 - if (node.tags) {
4256 - for (var j in node.tags) {
4257 - var tag = node.tags[j];
4258 - if (sort == 4) { if (mesh2) { tag = mesh2.name + ' - ' + node.tags[j]; } else { tag = '**INDV*~*DEVS** - ' + node.tags[j]; } }
4259 - var collapsed = CollapsedGroups['tag:' + encodeURIComponentEx(tag)];
4260 - var r2 = r.replace('**xx**xx*TaG*xx**xx**', encodeURIComponentEx(tag) + (collapsed?' style=display:none':''));
4261 - if (groups[tag] == null) { groups[tag] = r2; groupCount[tag] = 1; } else { groups[tag] += r2; groupCount[tag] += 1; }
4262 - if ((view == 3) || (view == 5)) break;
4263 - }
4264 - } else {
4265 - // If sorted by "Groups-Tags" and a device has no tags, put in a group only section.
4266 - if ((sort == 4) && (mesh2 != null) && ((node.tags == null) || (node.tags.length == 0))) {
4267 - var tag = mesh2.name;
4268 - var collapsed = CollapsedGroups['tag:' + encodeURIComponentEx(tag)];
4269 - var r2 = r.replace('**xx**xx*TaG*xx**xx**', encodeURIComponentEx(tag) + (collapsed?' style=display:none':''));
4270 - if (groups[tag] == null) { groups[tag] = r2; groupCount[tag] = 1; } else { groups[tag] += r2; groupCount[tag] += 1; }
4271 - }
4272 - }
4273 - r = '';
4274 - }
4275 -
4276 - deviceHeaderTotal++;
4277 - if (typeof deviceHeaderCount[node.state] == 'undefined') { deviceHeaderCount[node.state] = 1; } else { deviceHeaderCount[node.state]++; }
4278 - }
4279 -
4280 - if ((view == 1) && (current != null)) { r += '</div>'; } // Close collapse div
4281 -
4282 - // Above 32 devices, gray out the auto connect feature.
4283 - if (kvmDivs.length >= 32) { Q('autoConnectDesktopCheckbox').checked = false; }
4284 - QE('autoConnectDesktopCheckbox', kvmDivs.length < 32);
4285 -
4286 - // If displaying devices by groups, sort the group names and display the devices.
4287 - if ((sort == 3) || (sort == 4)) {
4288 - var groupNames = [], tagDeviceHeaderId = 0;
4289 - for (var i in groups) { groupNames.push(i); }
4290 - //groupNames.sort(function (a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); });
4291 - groupNames.sort(function (a, b) { return sortCollator.compare(a.toLowerCase(), b.toLowerCase()); });
4292 - for (var j in groupNames) {
4293 - var i = groupNames[j];
4294 - if (view == 2) {
4295 - r += '<tr><td colspan=' + colcount + '><div class=DevSt style=width:100%;padding-top:4px>';
4296 - var collapsed = CollapsedGroups['tag:' + encodeURIComponentEx(i)];
4297 - r += '<img class=collapseImage cmenu=expandAllContextMenu id="DevxColImg' + tagDeviceHeaderId + '" src=images/c' + ((collapsed === true)?'1':'2') + '.png height=8 width=8 style=margin-left:2px;margin-right:2px;cursor:pointer onclick=toggleCollapseGroup("' + tagDeviceHeaderId + '","tag:' + encodeURIComponentEx(i) + '",2)></img>'; // Collapse action
4298 - r += '<span class=devHeaderx style=float:right>' + groupCount[i] + ' node' + ((groupCount[i] > 1) ? 's' : '') + '</span><span>' + EscapeHtml(i).split('|').join(' &rarr; ').split('**INDV*~*DEVS**').join('<i>' + "Individual Devices" + '</i>') + '</span></div>' + groups[i];
4299 - } else {
4300 - r += '<div class=DevSt style=width:100%;padding-top:4px><span class=devHeaderx style=float:right>' + format(((groupCount[i] > 1) ? '{0} nodes' : '{0} node'), groupCount[i]) + '</span>';
4301 - var collapsed = CollapsedGroups['tag:' + encodeURIComponentEx(i)];
4302 - r += '<img class=collapseImage cmenu=expandAllContextMenu id="DevxColImg' + tagDeviceHeaderId + '" src=images/c' + ((collapsed === true)?'1':'2') + '.png height=8 width=8 style=margin-left:2px;margin-right:2px;cursor:pointer onclick=toggleCollapseGroup("' + tagDeviceHeaderId + '","tag:' + encodeURIComponentEx(i) + '")></img>'; // Collapse action
4303 - r += '<span>' + EscapeHtml(i).split('|').join(' &rarr; ').split('**INDV*~*DEVS**').join('<i>' + "Individual Devices" + '</i>') + '</span></div>';
4304 - r += '<div id=DevxCol' + tagDeviceHeaderId + ((collapsed === true)?' style=display:none':'') + '>' + groups[i] + '</div>'; // Open collapse div
4305 - }
4306 - tagDeviceHeaderId++;
4307 - }
4308 - }
4309 -
4310 - // If there is nothing to display, explain the problem
4311 - var viewNothing = false;
4312 - if ((r == '') && (nodes.length > 0) && ((Q('SearchInput').value != '') || (Q('DevFilterSelect').value != 0))) {
4313 - viewNothing = true;
4314 - if (sort == 3) {
4315 - r = '<div style="margin:30px">' + "No devices are included in any groups, click on a device's \"Groups\" to add to a group." + '</div>';
4316 - } else {
4317 - r = '<div style="margin:30px">' + "No devices matching this search." + ' <a onclick=clearDeviceSearch()>' + "Clear search filter" + '</a></div>';
4318 - }
4319 - }
4320 -
4321 - if ((view == 1) && (c == 2)) r += '<td><div style=width:301px></div></td>'; // Adds device padding
4322 -
4323 - // Display all empty device groups, we need to do this because users can add devices to these at any time.
4324 - if ((sort == 0) && ((Q('SearchInput').value == '') && (Q('DevFilterSelect').value == 0)) && (view < 3)) {
4325 - var deviceHeaderId2 = deviceHeaderId, sortedMeshes = [];
4326 - for (var i in meshes) { sortedMeshes.push(meshes[i]); }
4327 - sortedMeshes.sort(nameSort);
4328 - for (var i in sortedMeshes) {
4329 - var mesh = sortedMeshes[i], meshrights = GetMeshRights(mesh);
4330 - if (displayedMeshes[mesh._id] == null) {
4331 - if ((current != '') && (r != '')) { r += '</tr></table>'; }
4332 - r += '<table style=width:100%;padding-top:4px cellpadding=0 cellspacing=0><tr><td colspan=3 class=DevSt>';
4333 -
4334 - // Collapsing header & start collapsing area
4335 - deviceHeaderId2++;
4336 - var collapsed = CollapsedGroups[mesh._id];
4337 - r += '<img class=collapseImage cmenu=expandAllContextMenu id="DevxColImg' + deviceHeaderId2 + '" src=images/c' + ((collapsed === true)?'1':'2') + '.png height=8 width=8 style=margin-left:2px;margin-right:2px;cursor:pointer onclick=toggleCollapseGroup("' + deviceHeaderId2 + '","' + mesh._id + '")></img>'; // Collapse action
4338 -
4339 - r += '<span id=MxMESH style=cursor:pointer onclick=gotoMesh("' + mesh._id + '")>' + EscapeHtml(mesh.name) + '</span><span>';
4340 - r += getMeshActions(mesh, meshrights);
4341 - r += '</span></td></tr><tr>';
4342 - if (mesh.mtype == 1) {
4343 - r += '<td><div style=padding:10px><i>' + "No Intel&reg; AMT devices in this device group";
4344 - if (((meshrights & 4) != 0) && ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 4096) == 0))) { r += ', <a href=# style=cursor:pointer onclick=\'return addDeviceToMesh("' + mesh._id + '")\'>' + "add one" + '</a>'; }
4345 - } else if (mesh.mtype == 2) {
4346 - r += '<td><div id=DevxCol' + deviceHeaderId2 + ((collapsed === true)?' style=display:none':'') + '>'; // Open collapse div
4347 - r += '<div style=padding:10px><i>' + "No devices in this device group";
4348 - if (((meshrights & 4) != 0) && ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 4096) == 0))) { r += ', <a href=# style=cursor:pointer onclick=\'return addAgentToMesh("' + mesh._id + '")\'>' + "add one" + '</a>'; }
4349 - } else if (mesh.mtype == 3) {
4350 - r += '<td><div id=DevxCol' + deviceHeaderId2 + ((collapsed === true)?' style=display:none':'') + '>'; // Open collapse div
4351 - r += '<div style=padding:10px><i>' + "No local devices in this device group";
4352 - if (((meshrights & 4) != 0) && ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 4096) == 0))) { r += ', <a href=# style=cursor:pointer onclick=\'return addLocalDeviceToMesh("' + mesh._id + '")\'>' + "add one" + '</a>'; }
4353 - } else if (mesh.mtype == 4) {
4354 - r += '<td><div id=DevxCol' + deviceHeaderId2 + ((collapsed === true)?' style=display:none':'') + '>'; // Open collapse div
4355 - r += '<div style=padding:10px><i>' + "No devices in this device group";
4356 - }
4357 - r += '.</i></div></td>';
4358 - r += '</div>'; // End collapsing area
4359 -
4360 - current = mesh._id;
4361 - count++;
4362 - }
4363 - }
4364 - }
4365 -
4366 - if ((r != '') && (viewNothing == false)) {
4367 - r += '</table>';
4368 - if (view == 2) {
4369 - var colums = '';
4370 -
4371 - // Use defaults if needed
4372 - if (deviceViewSettings == null) { deviceViewSettings = {}; }
4373 - if (!Array.isArray(deviceViewSettings.devsCols)) { deviceViewSettings.devsCols = ['user','ip','conn']; }
4374 -
4375 - // Display configured columns
4376 - if (deviceViewSettings.devsCols.indexOf('agtype') >= 0) { colums += '<th style=color:gray;width:100px>' + "Agent Type"; }
4377 - if (deviceViewSettings.devsCols.indexOf('agver') >= 0) { colums += '<th style=color:gray;width:100px>' + "Agent Version"; }
4378 - if (deviceViewSettings.devsCols.indexOf('os') >= 0) { colums += '<th style=color:gray;width:160px>' + "OS"; }
4379 - if (deviceViewSettings.devsCols.indexOf('links') >= 0) { colums += '<th style=color:gray;width:120px>' + "Links"; }
4380 - if (deviceViewSettings.devsCols.indexOf('user') >= 0) { colums += '<th style=color:gray;width:120px>' + "User"; }
4381 - if (deviceViewSettings.devsCols.indexOf('ip') >= 0) { colums += '<th style=color:gray;width:120px>' + "Address"; }
4382 - if (deviceViewSettings.devsCols.indexOf('conn') >= 0) { colums += '<th style=color:gray;width:100px>' + "Connectivity"; }
4383 - if (deviceViewSettings.devsCols.indexOf('lastseen') >= 0) {
4384 - colums += '<th style=color:gray;width:120px>' + "Last Seen";
4385 - if (requestedLastConnects == false) { requestedLastConnects = true; meshserver.send({ action: 'lastconnects' }); }
4386 - }
4387 -
4388 - // This height of 1 div at the end to fix a problem in Linux firefox browsers
4389 - r = '<table style=width:100%;margin-top:4px cellpadding=0 cellspacing=0><th style=color:gray>' + colums + r + '</tr></table><div style=height:1px></div>';
4390 - }
4391 - } else {
4392 - r += '</table>';
4393 - if (sort == 3) { r = '<div style="margin:10px"><i>' + "No devices with tags found." + '</i></div>'; }
4394 - }
4395 -
4396 - // Add a "Add Device Group" option
4397 - r += '<div style=border-top-style:solid;border-top-width:1px;border-top-color:#DDDDDD;cursor:pointer;font-size:small;margin-top:4px>';
4398 - if ((view < 3) && (sort == 0) && (Object.keys(meshes).length > 0) && ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 64) == 0))) {
4399 - r += '<a href=# onclick="return account_createMesh()" title="' + "Create a new group of devices." + '" style=cursor:pointer>' + "Add Device Group" + '</a>&nbsp';
4400 - }
4401 - if ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 128) == 0)) {
4402 - r += '<a href=# onclick=\'return p10showMeshCmdDialog(0)\' style=cursor:pointer title="' + "Download MeshCmd, a command line tool that performs many functions." + '">' + "MeshCmd" + '</a>&nbsp';
4403 - if ((navigator.platform.toLowerCase() == 'win32') || (navigator.platform.toLowerCase() == 'macintel')) { r += '<a href=# onclick=\'return p10showMeshRouterDialog()\' style=cursor:pointer title="' + "Download MeshCentral Router, a TCP port mapping tool." + '">' + "Router" + '</a>&nbsp'; }
4404 - }
4405 - r += '</div><br/>';
4406 -
4407 - QH('xdevices', r);
4408 - deviceHeaderSet();
4409 -
4410 - for (var i in deviceHeaders) { QH(i, deviceHeaders[i]); }
4411 - for (var i in deviceHeadersTitles) { Q(i).title = deviceHeadersTitles[i]; }
4412 - p1updateInfo();
4413 -
4414 - // Take care of KVM surfaces in desktop view mode
4415 - if (((view == 3) || (view == 5)) && (xxcurrentView == 1)) {
4416 -
4417 - // Figure out and adjust the size to fill the width of the div
4418 - var vsize = [{ x: 180, y: 101 }, { x: 302, y: 169 }, { x: 454, y: 255 }][Q('sizeselect').selectedIndex];
4419 - if ((view == 3)) { // If we are in fixed width mode, correct the size for the screen width.
4420 - var realw = vsize.x + 2, tw = totalDeviceViewWidth - 5, xw = Math.floor(tw / realw);
4421 - xw = realw + Math.floor((tw - (xw * realw)) / xw);
4422 - vsize.y = vsize.y * (xw / vsize.x);
4423 - vsize.x = xw;
4424 - }
4425 -
4426 - for (var i in multiDesktop) { multiDesktop[i].xxdelete = true; }
4427 - for (var i in kvmDivs) {
4428 - var id = kvmDivs[i], shortid = id.split('/')[2], desk = multiDesktop[id];
4429 - if (desk != null) {
4430 - // This device already has a canvas, use it.
4431 - var width = (view == 5)?((desk.m.width * vsize.y) / desk.m.height):vsize.x;
4432 - desk.m.CanvasId.setAttribute('style', 'background-color:black;width:' + width + 'px;height:' + vsize.y + 'px');
4433 - Q('xkvmid_' + shortid).appendChild(desk.m.CanvasId);
4434 - delete desk.xxdelete;
4435 - QH('skvmid_' + shortid, ["Disconnected", "Connecting...", "Setup...", '', ''][((desk.m.State == null)?desk.m.state:desk.m.State)]);
4436 - } else {
4437 - var node = getNodeFromId(id);
4438 - if ((desktopNode == node) && (desktop != null)) { // Check if the main desktop is this device, if it is, use that.
4439 - // This device already has a canvas, use it.
4440 - var c = desktop.m.CanvasId;
4441 - var width = (view == 5)?((desktop.m.width * vsize.y) / desktop.m.height):vsize.x;
4442 - c.setAttribute('id', 'kvmid_' + shortid);
4443 - c.setAttribute('style', 'background-color:black;width:' + width + 'px;height:' + vsize.y + 'px');
4444 - c.setAttribute('onclick', 'toggleKvmDevice(\'' + id + '\')');
4445 - c.removeAttribute('onmousedown');
4446 - c.removeAttribute('onmouseup');
4447 - c.removeAttribute('onmousemove');
4448 - Q('xkvmid_' + shortid).appendChild(c);
4449 - QH('skvmid_' + shortid, ["Disconnected", "Connecting...", "Setup...", '', ''][((desktop.m.State == null)?desktop.m.state:desktop.m.State)]);
4450 - if (desktop.m.SendCompressionLevel) { desktop.m.SendCompressionLevel(webpSupport?4:1, multidesktopsettings.quality, multidesktopsettings.scaling, multidesktopsettings.framerate); }
4451 - desktop.shortid = shortid;
4452 - desktop.onStateChanged = onMultiDesktopStateChange;
4453 - desktop.m.onRemoteInputLockChanged = null;
4454 - desktop.m.onKeyboardStateChanged = null;
4455 - multiDesktop[id] = desktop;
4456 - desktop = desktopNode = currentNode = null;
4457 - // Setup a replacement desktop
4458 - QH('DeskParent', '<canvas id="Desk" oncontextmenu="return false" onmousedown=dmousedown(event) onmouseup=dmouseup(event) onmousemove=dmousemove(event)></canvas>');
4459 - } else {
4460 - // This is a new device, create a canvas for it.
4461 - var c = document.createElement('canvas');
4462 - c.setAttribute('id', 'kvmid_' + shortid);
4463 - c.setAttribute('width', 640);
4464 - c.setAttribute('height', 480);
4465 - c.setAttribute('oncontextmenu', 'return false');
4466 - c.setAttribute('style', 'background-color:black;width:' + vsize.x + 'px;height:' + vsize.y + 'px');
4467 - c.setAttribute('onclick', 'toggleKvmDevice(\'' + id + '\')');
4468 - try { Q('xkvmid_' + shortid).appendChild(c); } catch (ex) {}
4469 - // Check if we need to auto-connect
4470 - if (Q('autoConnectDesktopCheckbox').checked == true) { setTimeout(function() { connectMultiDesktop(node, 1); }, 100); }
4471 - }
4472 - }
4473 - }
4474 - for (var i in multiDesktop) {
4475 - // If a device is no longer viewed, disconnect it.
4476 - if (multiDesktop[i].xxdelete == true) { multiDesktop[i].Stop(); delete multiDesktop[i]; }
4477 - else if (debugmode && multiDesktop[i].m && multiDesktop[i].m.onScreenSizeChange) {
4478 - mdeskAdjust(multiDesktop[i].m, multiDesktop[i].m.ScreenWidth, multiDesktop[i].m.ScreenHeight, multiDesktop[i].m.CanvasId); // Adjust screen size change
4479 - }
4480 - }
4481 - deskAdjust();
4482 - } else {
4483 - disconnectAllKvmFunction();
4484 - Q('autoConnectDesktopCheckbox').checked = false;
4485 - }
4486 - }
4487 - oldviewmode = view;
4488 - onDevicesScrollEx();
4489 - }
4490 -
4491 -
4492 - var onDevicesTouchActive = false;
4493 - var onDevicesScrollnagleTimer = null;
4494 - function onDevicesScroll() {
4495 - if ((onDevicesScrollnagleTimer != null) || (onDevicesTouchActive)) return;
4496 - onDevicesScrollnagleTimer = setTimeout(onDevicesScrollEx, 250);
4497 - }
4498 -
4499 - function onDeviceTouch(x) {
4500 - if (onDevicesTouchActive == x) return;
4501 - onDevicesTouchActive = x;
4502 - if (x == false) onDevicesScrollEx();
4503 - }
4504 -
4505 - function onDevicesScrollEx() {
4506 - if (onDevicesScrollnagleTimer != null) { clearTimeout(onDevicesScrollnagleTimer); onDevicesScrollnagleTimer = null; }
4507 - var visibleTop = Q('xdevices').scrollTop - 200, visibleBottom = Q('xdevices').scrollTop + Q('xdevices').clientHeight + 200;
4508 - var view = Q('viewselect').value, devdivs = document.getElementsByName('xxdevice' + view);
4509 - for (var i = 0; i < devdivs.length; i++) {
4510 - if ((devdivs[i].offsetTop >= visibleTop) && (devdivs[i].offsetTop < visibleBottom)) {
4511 - var node = getNodeFromId(devdivs[i].id.substring(3));
4512 - if (node != null) { updateDeviceViewHtml(view, devdivs[i], node); } else { devdivs[i].innerHTML = ''; }
4513 - } else {
4514 - devdivs[i].innerHTML = '';
4515 - }
4516 - }
4517 - }
4518 -
4519 - // Update a single device in the current view
4520 - function updateDeviceViewDevice(node) {
4521 - var view = Q('viewselect').value;
4522 - if ((view != 1) && (view != 2)) { mainUpdate(4); return; }
4523 - if (typeof node == 'string') { node = getNodeFromId(node); }
4524 - if (node == null) return;
4525 - var devdiv = Q('xv' + view + node._id);
4526 - if ((devdiv != null) && (devdiv.innerHTML != '')) { updateDeviceViewHtml(view, devdiv, node); } // Only update if the device is visible
4527 - }
4528 -
4529 - function updateDeviceViewHtml(view, div, node) {
4530 - var title = EscapeHtml(node.name);
4531 - if (title.length == 0) { title = '<i>' + "None" + '</i>'; }
4532 - if ((node.rname != null) && (node.rname.length > 0)) { title += ' / ' + EscapeHtml(node.rname); }
4533 - var name = EscapeHtml(node.name);
4534 - if (showRealNames == true && node.rname != null) name = EscapeHtml(node.rname);
4535 - if (name.length == 0) { name = '<i>' + "None" + '</i>'; }
4536 - if (((view == 1) && (div.parentNode.style['display'] == 'none')) || ((view == 2) && (div.style['display'] == 'none'))) { div.innerHTML = ''; return; } // If this section is collapsed, don't render
4537 -
4538 - // Add device notification icons
4539 - var devNotify = '', devNotifySub = '';
4540 -
4541 - // This device is "starred"
4542 - if (stars[node._id] == 1) {
4543 - if (view == 2) {
4544 - devNotifySub += '<img class=deviceNotifySmallDotSub src=images/icon-star-notify-10.png width=10 height=10>';
4545 - } else {
4546 - devNotifySub += '<img class=deviceNotifyDotSub src=images/icon-star-notify-16.png width=16 height=16>';
4547 - }
4548 - }
4549 -
4550 - // This device has session information
4551 - if (node.sessions != null) {
4552 - // Display any agent messages
4553 - if (node.sessions.msg != null) {
4554 - if (view == 2) {
4555 - devNotifySub += '<div onclick=showDeviceMessages(\'' + node._id + '\',null,event) style="width:10;height:10" class=deviceNotifySmallDotSub></div>';
4556 - } else {
4557 - devNotifySub += '<div onclick=showDeviceMessages(\'' + node._id + '\',null,event) style="width:16;height:16" class=deviceNotifyDotSub>' + Object.keys(node.sessions.msg).length + '</div>';
4558 - }
4559 - }
4560 -
4561 - // Sessions are active
4562 - if ((node.sessions.kvm != null) || (node.sessions.terminal != null) || (node.sessions.files != null) || (node.sessions.tcp != null) || (node.sessions.udp != null)) {
4563 - if (view == 2) {
4564 - devNotifySub += '<img onclick=showDeviceSessions(\'' + node._id + '\',null,event) class=deviceNotifySmallDotSub src=images/icon-relay-notify10.png width=10 height=10>';
4565 - } else {
4566 - devNotifySub += '<img onclick=showDeviceSessions(\'' + node._id + '\',null,event) class=deviceNotifyDotSub src=images/icon-relay-notify.png width=16 height=16>';
4567 - }
4568 - }
4569 -
4570 - // Help is required
4571 - if (node.sessions.help != null) {
4572 - if (view == 2) {
4573 - devNotifySub += '<img onclick=showDeviceHelpRequests(\'' + node._id + '\',null,event) class=deviceNotifySmallDotSub src=images/icon-help-notify-10.png width=10 height=10>';
4574 - } else {
4575 - devNotifySub += '<img onclick=showDeviceHelpRequests(\'' + node._id + '\',null,event) class=deviceNotifyDotSub src=images/icon-help-notify-16.png width=16 height=16>';
4576 - }
4577 - }
4578 -
4579 - // Battery state
4580 - if ((node.sessions.battery != null) && (view == 1)) {
4581 - var bat = node.sessions.battery;
4582 - var statestr = '';
4583 - if (bat.state == 'ac') { statestr = "Device is plugged-in"; }
4584 - if (bat.state == 'dc') { statestr = "Device is battery powered"; }
4585 -
4586 - var levelstr = '', levelnum = -1;
4587 - if ((typeof bat.level == 'number') && (bat.level >= 0) && (bat.level <= 100)) {
4588 - levelstr = bat.level + '%';
4589 - levelnum = (Math.floor((bat.level + 10) / 25) + 1);
4590 - if (levelnum > 5) { lvl = 5; }
4591 - if (bat.state == 'ac') { if (bat.level == 100) { levelnum = 11; } else { levelnum += 5; } }
4592 - }
4593 -
4594 - if (levelnum > 0) {
4595 - devNotify += '<div class="deviceBatterySmall deviceBatterySmall' + levelnum + '" title="' + ((statestr != null)?(statestr + ', ' + levelstr):levelstr) + '"></div>';
4596 - }
4597 - }
4598 - }
4599 -
4600 - // Add any device icons
4601 - if (devNotifySub != '') {
4602 - if (view == 2) {
4603 - devNotify += '<div class=deviceNotifySmallDot>' + devNotifySub + '</div>';
4604 - } else {
4605 - devNotify += '<div class=deviceNotifyDot>' + devNotifySub + '</div>';
4606 - }
4607 - }
4608 -
4609 - // Node
4610 - var icon = node.icon;
4611 - if (((!node.conn) || (node.conn == 0)) && (node.mtype != 3)) { icon += ' gray'; }
4612 -
4613 - if (view == 1) {
4614 - div.innerHTML = '<table id=devs cmenu=devsContentMenu onmouseover=devMouseHover(this,1) onmouseout=devMouseHover(this,0) style=width:100%;height:100%><tr><td style=width:22px><input class="' + node.meshid + ' DeviceCheckbox" onchange=p1devcheck(event) value=devid_' + node._id + ' type=checkbox ' + (checkedNodeids[node._id]?' checked':'') + '></td><td><table onclick=gotoDevice(\'' + node._id + '\',null,null,event) border=0 cellspacing=0 style=width:100%;height:100%;cursor:pointer><tr><td style=width:50px tabindex=0 onkeypress="if (event.key==\'Enter\') gotoDevice(\'' + node._id + '\',null,null,event)"><div class="i' + icon + '" style=width:50px></div></td><td class=g1t></td><td class=e2t><div class=e1t style=width:' + ((div.clientWidth) - 120) + 'px title="' + title + '">' + name + '</div><div>' + NodeStateStr(node) + '</div></td><td class=g2t></td></tr></table></td></tr></table>' + devNotify;
4615 - } else if (view == 2) {
4616 - var states = [];
4617 - if (node.conn) {
4618 - if ((node.conn & 1) != 0) {
4619 - if (node.mtype == 4) {
4620 - if (node.porttype == 'PDU') {
4621 - states.push('<span title="' + "Switch port is ready for use." + '">' + "Switch" + '</span>');
4622 - } else {
4623 - states.push('<span title="' + "IP-KVM port is connected and ready for use." + '">' + "IP-KVM" + '</span>');
4624 - }
4625 - } else {
4626 - states.push('<span title="' + "Mesh agent is connected and ready for use." + '">' + "Agent" + '</span>');
4627 - }
4628 - }
4629 - if ((node.conn & 2) != 0) { states.push('<span title="' + "Intel&reg; AMT CIRA is connected and ready for use." + '">' + "CIRA" + '</span>'); }
4630 - else if ((node.conn & 4) != 0) { states.push('<span title="' + "Intel&reg; AMT is routable." + '">' + "AMT" + '</span>'); }
4631 - if ((node.conn & 8) != 0) { states.push('<span title="' + "Mesh agent is reachable using another agent as relay." + '">' + "Relay" + '</span>'); }
4632 - if ((node.conn & 16) != 0) { states.push('<span title="' + "MQTT connection to the device is active." + '">' + "MQTT" + '</span>'); }
4633 - }
4634 - if (node.mtype == 3) {
4635 - var mesh = meshes[node.meshid];
4636 - if (mesh && mesh.relayid) {
4637 - states.push('<span title="' + "Local network connection thru a relay agent." + '">' + "Relay" + '</span>');
4638 - } else {
4639 - states.push('<span title="' + "Local network connection." + '">' + "Local" + '</span>');
4640 - }
4641 - }
4642 -
4643 - if (node.desc && (deviceViewSettings.devsCols.indexOf('desc') >= 0)) { name = '<div style=float:right>' + EscapeHtml(node.desc) + '</div><div>' + name + '</div>'; }
4644 -
4645 - var collapseName = node.meshid;
4646 - if (sort == 1) { collapseName = ('pwr:' + (node.pwr?node.pwr:0)); }
4647 - else if ((sort == 3) || (sort == 4)) { collapseName = 'tag:**xx**xx*TaG*xx**xx**'; }
4648 - var collapsed = (sort != 3) & (sort != 4) & CollapsedGroups[collapseName];
4649 - var r = '<td style=position:relative><div id=devs cmenu=devsContentMenu class=bar18 tabindex=0 onmouseover=devMouseHover(this,1) onmouseout=devMouseHover(this,0) style=height:18px;width:100%;font-size:medium onkeypress="if (event.key==\'Enter\') gotoDevice(\'' + node._id + '\',null,null,event)">';
4650 - r += '<div class=deviceBarCheckbox><input class="' + node.meshid + ' DeviceCheckbox" value=devid_' + node._id + ' type=checkbox onchange=p1devcheck(event) ' + (checkedNodeids[node._id]?' checked':'') + '></div>';
4651 - r += '<div class=deviceBarIcon onclick=gotoDevice(\'' + node._id + '\',null,null,event)><div class="j' + icon + '" style=width:16px;margin-top:1px;margin-left:2px;height:16px></div></div>';
4652 - r += '<div class=g1 style=height:18px;float:left></div><div class=g2 style=height:18px;float:right></div>';
4653 - r += '<div class=style10 style=cursor:pointer;font-size:14px;max-height:18px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap title="' + title + '" onclick=gotoDevice(\'' + node._id + '\',null,null,event)>' + name + '</div></div>' + devNotify + '</td>';
4654 -
4655 - // Use defaults if needed
4656 - if (deviceViewSettings == null) { deviceViewSettings = {}; }
4657 - if (!Array.isArray(deviceViewSettings.devsCols)) { deviceViewSettings.devsCols = ['user','ip','conn']; }
4658 -
4659 - // Display configured columns
4660 - if (deviceViewSettings.devsCols.indexOf('agtype') >= 0) { r += '<td style=text-align:center;font-size:x-small;padding-left:4px;padding-right:4px title="' + EscapeHtml(((node.mtype != 3) && node.agent && node.agent.id && (node.agent.id <= agentsStr.length))?agentsStr[node.agent.id]:'') + '">' + EscapeHtml(((node.mtype != 3) && node.agent && node.agent.id && (node.agent.id <= agentsStr.length))?agentsStr[node.agent.id]:'').replace(',', '<br />'); } // Agent type
4661 - if (deviceViewSettings.devsCols.indexOf('agver') >= 0) { r += '<td style=text-align:center;font-size:x-small;padding-left:4px;padding-right:4px title="' + EscapeHtml((node.agent && node.agent.core)?node.agent.core:'') + '">' + EscapeHtml((node.agent && node.agent.core)?node.agent.core:'').replace(',', '<br />'); } // Agent core
4662 - if (deviceViewSettings.devsCols.indexOf('os') >= 0) { // Operating System
4663 - if (node.mtype == 3) {
4664 - var osstr = ''; if (node.agent.id == 4) { osstr = 'Windows'; } if (node.agent.id == 6) { osstr = 'Linux'; } if (node.agent.id == 29) { osstr = 'MacOS'; }
4665 - r += '<td style=text-align:center;font-size:x-small title="' + EscapeHtml(osstr) + '">' + EscapeHtml(osstr);
4666 - } else {
4667 - r += '<td style=text-align:center;font-size:x-small title="' + EscapeHtml(node.osdesc?node.osdesc:'') + '">' + EscapeHtml(node.osdesc?node.osdesc:'');
4668 - }
4669 - }
4670 - if (deviceViewSettings.devsCols.indexOf('links') >= 0) { r += '<td style=text-align:center;font-size:x-small>' + getShortRouterLinks(node); } // Links
4671 - if (deviceViewSettings.devsCols.indexOf('user') >= 0) { r += '<td style=text-align:center>' + getUserShortStr(node); } // User
4672 - if (deviceViewSettings.devsCols.indexOf('ip') >= 0) { var ip = ''; if (node.mtype == 3) { ip = node.host; } else if (node.ip) { ip = node.ip; } r += '<td style=text-align:center>' + ip; } // IP address
4673 - if (deviceViewSettings.devsCols.indexOf('conn') >= 0) { r += '<td style=text-align:center>' + states.join('&nbsp;+&nbsp;'); } // Connectivity
4674 - if (deviceViewSettings.devsCols.indexOf('lastseen') >= 0) { r += '<td style=text-align:center;font-size:x-small>'; if (node.conn > 0) { r += "Connected"; } else if (node.lastconnect != null) { r += printDateTime(new Date(node.lastconnect)); } }
4675 -
4676 - div.innerHTML = r;
4677 - } else if ((view == 3) || (view == 5)) {
4678 - // Draw the device and canvas
4679 - var r = '<div id=devs cmenu=devsContentMenu style=display:inline-block;position:relative;margin:1px;background-color:lightgray;border-radius:5px;position:relative><div tabindex=0 style=padding:3px;cursor:pointer onclick=gotoDevice(\'' + node._id + '\',11,null,event) onkeypress="if (event.key==\'Enter\') gotoDevice(\'' + node._id + '\',11,null,event)">' + devNotify;
4680 - //r += '<input class="' + node.meshid + ' DeviceCheckbox" onclick=p1updateInfo() value=devid_' + node._id + ' type=checkbox style=float:left>';
4681 - r += '<div class="j' + icon + '" style=width:16px;float:left></div>&nbsp;' + name + '</div>';
4682 - r += '<span onclick=gotoDevice(\'' + node._id + '\',null,null,event)></span><div id=xkvmid_' + node._id.split('/')[2] + '><div id=skvmid_' + node._id.split('/')[2] + ' tabindex=0 style="position:absolute;color:white;left:5px;top:27px;text-shadow:0px 0px 5px #000;z-index:10;cursor:default" onclick=toggleKvmDevice(\'' + node._id + '\') onkeypress="if (event.key==\'Enter\') toggleKvmDevice(\'' + node._id + '\')">' + "Disconnected" + '</div></div>';
4683 - r += '</div>';
4684 - return r;
4685 - }
4686 - }
4687 -
4688 - // Return HTML with a list of MeshCentral Router links
4689 - function getShortRouterLinks(node) {
4690 - var x = '', meshrights = GetNodeRights(node);
4691 -
4692 - // RDP link, show this link only of the remote machine is Windows.
4693 - if ((((node.conn & 1) != 0) || (node.mtype == 3)) && (node.agent) && ((meshrights & 8) != 0) && (node.agent.id != 14)) {
4694 - if (webRelayPort != 0) {
4695 - x += '<a href=# onclick=p10WebRouter("' + node._id + '",1,' + (node.httpport ? node.httpport : 80) + ')>' + "HTTP" + ((node.httpport && (node.httpport != 80)) ? '/' + node.httpport : '') + '</a>&nbsp;';
4696 - x += '<a href=# onclick=p10WebRouter("' + node._id + '",2,' + (node.httpsport ? node.httpsport : 443) + ')>' + "HTTPS" + ((node.httspport && (node.httpsport != 443)) ? '/' + node.httpsport : '') + '</a>&nbsp;';
4697 - }
4698 - if ((node.agent.id > 0) && (node.agent.id < 5)) {
4699 - if (navigator.platform.toLowerCase() == 'win32') {
4700 - if ((serverinfo.devicemeshrouterlinks == null) || (serverinfo.devicemeshrouterlinks.rdp != false)) {
4701 - x += '<a href=# cmenu=altPortContextMenu id=rdpMCRouterLink onclick=p10MCRouter("' + node._id + '",3)>' + "RDP" + '</a>&nbsp;';
4702 - }
4703 - }
4704 - }
4705 - if (node.agent.id > 4) {
4706 - if ((navigator.platform.toLowerCase() == 'win32') || (navigator.platform.toLowerCase() == 'macintel')) {
4707 - if ((serverinfo.devicemeshrouterlinks == null) || (serverinfo.devicemeshrouterlinks.ssh != false)) {
4708 - x += '<a href=# onclick=p10MCRouter("' + node._id + '",4,' + (node.sshport ? node.sshport : 22) + ')>' + "SSH" + '</a>&nbsp;';
4709 - }
4710 - }
4711 - if (navigator.platform.toLowerCase() == 'win32') {
4712 - if ((serverinfo.devicemeshrouterlinks == null) || (serverinfo.devicemeshrouterlinks.scp != false)) {
4713 - x += '<a href=# onclick=p10MCRouter("' + node._id + '",5,' + (node.sshport ? node.sshport : 22) + ')>' + "SCP" + '</a>&nbsp;';
4714 - }
4715 - }
4716 - }
4717 - if ((navigator.platform.toLowerCase() == 'win32') || (navigator.platform.toLowerCase() == 'macintel')) {
4718 - if ((serverinfo.devicemeshrouterlinks != null) && (Array.isArray(serverinfo.devicemeshrouterlinks.extralinks))) {
4719 - for (var i in serverinfo.devicemeshrouterlinks.extralinks) {
4720 - var r = serverinfo.devicemeshrouterlinks.extralinks[i], p = '\"' + r.protocol + '\"';
4721 - if (doesDeviceMatchFilterTags(node, r.filter)) {
4722 - if ((node.mtype == 3) && ((r.protocol == 'mcrdesktop') || (r.protocol == 'mcrfiles'))) continue;
4723 - if (typeof r.protocol == 'number') { p = r.protocol; } else if (r.protocol == 'http') { p = 1; } else if (r.protocol == 'https') { p = 2; } else if (r.protocol == 'rdp') { p = 3; } else if (r.protocol == 'ssh') { p = 4; } else if (r.protocol == 'scp') { p = 5; } else if (r.protocol == 'mcrdesktop') { p = 6; } else if (r.protocol == 'mcrfiles') { p = 7; }
4724 - x += '<a href=# onclick=p10MCRouter("' + node._id + '",' + p + ',' + r.port + (r.ip?(',\"' + r.ip + '\"'):',null') + ',' + (r.localport?r.localport:0) + ')>' + r.name + '</a>&nbsp;';
4725 - }
4726 - }
4727 - }
4728 - }
4729 - }
4730 -
4731 - return x;
4732 - }
4733 -
4734 - // Check if this device matches any of the given filters
4735 - function doesDeviceMatchFilterTags(node, filter) {
4736 - if (filter == null) return true; // No filters, every device matches.
4737 - if (!Array.isArray(filter)) return false; // Bad filter
4738 - if (filter.indexOf(node.meshid) >= 0) return true; // Device group match
4739 - if (filter.indexOf(node._id) >= 0) return true; // Nodeid match
4740 - if (Array.isArray(node.tags)) { for (var i in node.tags) { if (filter.indexOf('tag:' + node.tags[i]) >= 0) return true; } } // Tag match
4741 - return false;
4742 - }
4743 -
4744 - // Show device help requests
4745 - function showDeviceHelpRequests(nodeid, force, e) {
4746 - if (e) haltEvent(e);
4747 - if (xxdialogMode && !force) return false;
4748 - var node = null, x = '';
4749 - if (nodeid == null) { node = currentNode; } else { node = getNodeFromId(nodeid); }
4750 - if ((node == null) || (node.sessions == null)) { setDialogMode(0); return false; }
4751 - if (node.sessions.help != null) { for (var j in node.sessions.help) { x += '<div style="background-color:#CCC;padding:6px;border-radius:6px"><a style="float:right" onclick=closeDeviceHelpRequest("' + encodeURIComponentEx(j) + '","' + encodeURIComponentEx(node._id) + '")>' + "Dismiss" + '</a><div style=margin-bottom:6px><b>' + EscapeHtml(j) + '</b></div><div style=margin-bottom:6px>' + EscapeHtml(node.sessions.help[j]) + '</div></div>'; } }
4752 - if (x != '') { setDialogMode(2, "Help Requests" + ' - ' + EscapeHtml(node.name), 1, null, x, 'HELPREQ-' + node._id); } else { setDialogMode(0); }
4753 - return false;
4754 - }
4755 -
4756 - function closeDeviceHelpRequest(appid, nodeid) {
4757 - setDialogMode(0);
4758 - meshserver.send({ action: 'msg', type: 'localapp', nodeid: decodeURIComponent(nodeid), appid: decodeURIComponent(appid), value: { cmd: "cancelhelp" } });
4759 - }
4760 -
4761 - // Show currently active sessions on this device
4762 - function showDeviceSessions(nodeid, force, e) {
4763 - if (e) haltEvent(e);
4764 - if (xxdialogMode && !force) return false;
4765 - var node = null, x = '';
4766 - if (nodeid == null) { node = currentNode; } else { node = getNodeFromId(nodeid); }
4767 - if ((node == null) || (node.sessions == null)) { setDialogMode(0); return false; }
4768 - for (var i in node.sessions) {
4769 - if ((i == 'kvm') && (node.sessions.multidesk == null)) {
4770 - x += '<u>' + "Remote Desktop" + '</u>';
4771 - for (var j in node.sessions.kvm) {
4772 - if (j.startsWith('user/')) {
4773 - var trash = '';
4774 - if (((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF))) { trash = ' <a href=# onclick=\'return endDeviceSession("kvm", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
4775 - x += addHtmlValue2(getUserName(j), ((node.sessions.kvm[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.kvm[j]))) + trash);
4776 - } else if (j == 'busy') {
4777 - x += addHtmlValue2("Device is busy", ((node.sessions.kvm[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.kvm[j]))));
4778 - }
4779 - }
4780 - } else if (i == 'multidesk') {
4781 - x += '<u>' + "Remote Desktop" + '</u>';
4782 - for (var j in node.sessions.multidesk) {
4783 - var trash = '';
4784 - if ((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF)) { trash = ' <a href=# onclick=\'return endDeviceSession("multidesk", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
4785 - x += addHtmlValue2(getUserName(j), ((node.sessions.multidesk[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.multidesk[j]))) + trash);
4786 - }
4787 - } else if (i == 'terminal') {
4788 - x += '<u>' + "Terminal" + '</u>';
4789 - for (var j in node.sessions.terminal) {
4790 - var trash = '';
4791 - if ((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF)) { trash = ' <a href=# onclick=\'return endDeviceSession("terminal", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
4792 - x += addHtmlValue2(getUserName(j), ((node.sessions.terminal[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.terminal[j]))) + trash);
4793 - }
4794 - } else if (i == 'files') {
4795 - x += '<u>' + "Files" + '</u>';
4796 - for (var j in node.sessions.files) {
4797 - var trash = '';
4798 - if ((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF)) { trash = ' <a href=# onclick=\'return endDeviceSession("files", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
4799 - x += addHtmlValue2(getUserName(j), ((node.sessions.files[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.files[j]))) + trash);
4800 - }
4801 - } else if (i == 'tcp') {
4802 - x += '<u>' + "TCP Routing" + '</u>';
4803 - for (var j in node.sessions.tcp) {
4804 - var trash = '';
4805 - if ((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF)) { trash = ' <a href=# onclick=\'return endDeviceSession("tcp", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
4806 - x += addHtmlValue2(getUserName(j), ((node.sessions.tcp[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.tcp[j]))) + trash);
4807 - }
4808 - } else if (i == 'udp') {
4809 - x += '<u>' + "UDP Routing" + '</u>';
4810 - for (var j in node.sessions.udp) {
4811 - var trash = '';
4812 - if ((j == userinfo._id) || (GetNodeRights(node) == 0xFFFFFFFF)) { trash = ' <a href=# onclick=\'return endDeviceSession("udp", "' + encodeURIComponentEx(node._id) + '", "' + encodeURIComponentEx(j) + '")\' title="' + "Disconnect this session" + '" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
4813 - x += addHtmlValue2(getUserName(j), ((node.sessions.udp[j] == 1)?"1 session":nobreak(format("{0} sessions", node.sessions.udp[j]))) + trash);
4814 - }
4815 - }
4816 - }
4817 - if (x != '') { setDialogMode(2, "Sessions" + ' - ' + EscapeHtml(node.name), 1, null, x, 'SESSIONS-' + node._id); } else { setDialogMode(0); }
4818 - return false;
4819 - }
4820 -
4821 - function endDeviceSession(protocol, nodeid, userid) {
4822 - var userIdSplit = decodeURIComponent(userid).split('/'), uid = userIdSplit[0] + '/' + userIdSplit[1] + '/' + userIdSplit[2], guestname = null;
4823 - if ((userIdSplit.length == 4) && (userIdSplit[3].startsWith('guest:'))) { guestname = atob(userIdSplit[3].substring(6)); }
4824 - if (protocol == 'multidesk') {
4825 - meshserver.send({ action: 'endDesktopMultiplex', nodeid: decodeURIComponent(nodeid), xuserid: uid, guestname, guestname });
4826 - } else {
4827 - meshserver.send({ action: 'msg', type: 'endtunnel', nodeid: decodeURIComponent(nodeid), xuserid: uid, guestname, guestname, protocol: protocol });
4828 - }
4829 - }
4830 -
4831 - // Show currently active sessions on this device
4832 - function showDeviceMessages(nodeid, force, e) {
4833 - if (e) haltEvent(e);
4834 - if (xxdialogMode && !force) return false;
4835 - var node = null, x = '<div style=max-height:200px;overflow-y:auto>', count = 0;
4836 - if (nodeid == null) { node = currentNode; } else { node = getNodeFromId(nodeid); }
4837 - if ((node == null) || (node.sessions == null) || (node.sessions.msg == null)) { setDialogMode(0); return false; }
4838 - for (var i in node.sessions.msg) {
4839 - var msg = i, icon = 5;
4840 - if (typeof node.sessions.msg[i].msg == 'string') { msg = node.sessions.msg[i].msg; }
4841 - if ((typeof node.sessions.msg[i].msgid == 'number') && (eventsMessageId[node.sessions.msg[i].msgid] != null)) { msg = eventsMessageId[node.sessions.msg[i].msgid]; }
4842 - if (typeof node.sessions.msg[i].icon == 'number') { icon = node.sessions.msg[i].icon; }
4843 - if ((icon < 1) || (icon > 9)) { icon = 5; }
4844 - x += '<table style=width:96%><td style=width:24px><div class=NotifyIconSmall' + icon + '></div><td><div style="border-radius:5px;background-color:#BBB;width:100%;padding:8px">' + EscapeHtml(msg) + '</div></table>';
4845 - count++;
4846 - }
4847 - x += '</div>';
4848 - if (count > 0) setDialogMode(2, "Agent Messages" + ' - ' + EscapeHtml(node.name), 1, null, x, 'MESSAGES-' + node._id);
4849 - return false;
4850 - }
4851 -
4852 - function toggleCollapseGroup(id, id2, type) {
4853 - var x;
4854 - if (type == 2) {
4855 - // Table rows collapse
4856 - var xrows = document.getElementsByName('xxdevice2');
4857 - if (xrows.length == 0) return;
4858 - var rows = [];
4859 - for (var i = 0; i < xrows.length; i++) { if (xrows[i].attributes.colname.value.substring(7) == id2) { rows.push(xrows[i]); } }
4860 - var x = (rows[0].style['display'] == 'none');
4861 - if (x) { delete CollapsedGroups[id2]; } else { CollapsedGroups[id2] = true; }
4862 - for (var i = 0; i < rows.length; i++) { rows[i].style['display'] = (x ? '' : 'none'); }
4863 - } else {
4864 - // Simple DIV collapse
4865 - x = (QS('DevxCol' + id)['display'] == 'none');
4866 - if (x) { delete CollapsedGroups[id2]; } else { CollapsedGroups[id2] = true; }
4867 - QV('DevxCol' + id, x);
4868 - }
4869 - Q('DevxColImg' + id).src = x?'images/c2.png':'images/c1.png';
4870 - putstore('_collapse', JSON.stringify(CollapsedGroups));
4871 - updateCollapseAllButton();
4872 - onDevicesScrollEx();
4873 - onDevicesScrollEx(); // TODO: Not sure why, but second call is needed for proper update
4874 - }
4875 -
4876 - function toggleKvmDevice(node) {
4877 - if (typeof node == 'string') { node = getNodeFromId(node); } // Convert nodeid to node if needed
4878 - var rights = GetNodeRights(node);
4879 - if ((rights & 8) || (rights & 256)) { // Requires remote control rights or desktop view only rights
4880 - //var conn = 0;
4881 - //if ((node.conn & 1) != 0) { conn = 1; } else if ((node.conn & 6) != 0) { conn = 2; } // Check what type of connect we can do (Agent vs AMT)
4882 - if (node.conn & 1) { connectMultiDesktop(node, 1); }
4883 - }
4884 - }
4885 -
4886 - function getUserShortStr(node) {
4887 - if (node == null || node.users == null || (!Array.isArray(node.users)) || node.users.length == 0) return '';
4888 - if (node.users.length > 1) { return '<span title="' + EscapeHtml(node.users.join(', ')) + '">' + nobreak(format("{0} users", node.users.length)) + '</span>'; }
4889 - var u = node.users[0], su = u, i = u.indexOf('\\');
4890 - if (i > 0) { su = u.substring(i + 1); }
4891 - su = EscapeHtml(su);
4892 - if (su.length > 15) { su = su.substring(0, 14) + '&#8230;'; }
4893 - return '<span title="' + EscapeHtml(u) + '">' + su + '</span>';
4894 - }
4895 -
4896 - function autoConnectDesktops() { if (Q('autoConnectDesktopCheckbox').checked == true) { connectAllKvmFunction(); } }
4897 - function connectAllKvmFunction(force) {
4898 - if (xxdialogMode) return false;
4899 - if (force !== true) { // We need to count how many devices will need to be connected, if it's a lot, prompt first.
4900 - var count = 0;
4901 - for (var i in nodes) {
4902 - var node = nodes[i], nodeid = nodes[i]._id;
4903 - if ((multiDesktop[nodeid] == null) && ((Object.keys(checkedNodeids).length == 0) || checkedNodeids[nodeid])) {
4904 - var rights = GetNodeRights(node);
4905 - if ((rights & 8) || (rights & 256)) { // Requires remote control rights or desktop view only rights
4906 - //var conn = 0;
4907 - //if ((node.conn & 1) != 0) { conn = 1; } else if ((node.conn & 6) != 0) { conn = 2; } // Check what type of connect we can do (Agent vs AMT)
4908 - if ((node.conn & 1) && (node.v == true)) { count++; }
4909 - }
4910 - }
4911 - }
4912 - if (count > 8) { setDialogMode(2, "Connect All", 3, function() { connectAllKvmFunction(true); }, format("Are you sure you want to connect to {0} devices?", count)); return; }
4913 - }
4914 -
4915 - // Perform connect all
4916 - for (var i in nodes) {
4917 - if ((nodes[i].v == true) && (multiDesktop[nodes[i]._id] == null) && ((Object.keys(checkedNodeids).length == 0) || checkedNodeids[nodes[i]._id])) {
4918 - toggleKvmDevice(nodes[i]._id);
4919 - }
4920 - }
4921 - }
4922 - function disconnectAllKvmFunction() { if (xxdialogMode) return false; for (var nodeid in multiDesktop) { multiDesktop[nodeid].Stop(); } multiDesktop = {}; }
4923 - function onMultiDesktopStateChange(desk, state) { try { QH('skvmid_' + desk.shortid, ["Disconnected", "Connecting...", "Setup...", '', ''][state]); } catch (ex) {} }
4924 - function onDeviceSearchChanged(e) { if (e != null) { if (e.target.id == 'SearchInput') { Q('KvmSearchInput').value = Q('SearchInput').value; } else { Q('SearchInput').value = Q('KvmSearchInput').value; } } mainUpdate(5); }
4925 -
4926 - function showMultiDesktopSettings() {
4927 - QV('td7amtkvm', false);
4928 - QV('td7meshkvm', true);
4929 - QV('td7rdpkvm', false);
4930 - QV('d7desktopOtherSettings', false);
4931 - d7bitmapquality.value = multidesktopsettings.quality;
4932 - d7bitmapscaling.value = multidesktopsettings.scaling;
4933 - if (multidesktopsettings.framerate) { d7framelimiter.value = multidesktopsettings.framerate; } else { d7framelimiter.value = 100; }
4934 - setDialogMode(7, "Remote Desktop Settings", 3, showMultiDesktopSettingsChanged);
4935 - }
4936 -
4937 - function showMultiDesktopSettingsChanged() {
4938 - multidesktopsettings.quality = d7bitmapquality.value;
4939 - multidesktopsettings.scaling = d7bitmapscaling.value;
4940 - multidesktopsettings.framerate = d7framelimiter.value;
4941 - localStorage.setItem('multidesktopsettings', JSON.stringify(multidesktopsettings));
4942 - // Make changes to all current connections
4943 - for (var i in multiDesktop) { multiDesktop[i].m.SendCompressionLevel(webpSupport?4:1, multidesktopsettings.quality, multidesktopsettings.scaling, multidesktopsettings.framerate); }
4944 - }
4945 -
4946 - function connectMultiDesktop(node, contype) {
4947 - var nodeid = node._id, shortid = nodeid.split('/')[2];
4948 - var desk = multiDesktop[nodeid];
4949 - if (desk == null) {
4950 - if (Q('kvmid_' + shortid) == null) return; // Check if this device is being displayed, if not, exit now.
4951 - if (contype == 2) {
4952 - // Setup the Intel AMT remote desktop
4953 - if ((node.intelamt.user == null) || (node.intelamt.user == '')) { return; }
4954 - desk = CreateAmtRedirect(CreateAmtRemoteDesktop('kvmid_' + shortid), authCookie);
4955 - desk.shortid = shortid;
4956 - //desk.debugmode = debugmode;
4957 - desk.onStateChanged = onMultiDesktopStateChange;
4958 - desk.m.bpp = 1;
4959 - desk.m.useZRLE = true;
4960 - desk.m.showmouse = true;
4961 - desk.m.onKvmData = function (data) { console.log('KVM Data received in multi-desktop mode, this is not supported.'); }; // KVM Data Channel not supported in multi-desktop right now.
4962 - desk.m.onScreenSizeChange = mdeskAdjust; // Multi-Desktop Adjust
4963 - desk.Start(nodeid, 16994, '*', '*', 0);
4964 - desk.contype = 2;
4965 - multiDesktop[nodeid] = desk;
4966 - } else if (contype == 1) {
4967 - // Setup the Mesh Agent remote desktop
4968 - desk = CreateAgentRedirect(meshserver, CreateAgentRemoteDesktop('kvmid_' + shortid), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
4969 - desk.m.UseExtendedKeyFlag = (node.agent.id < 5); // Only use extended keys on Windows agents for now
4970 - desk.m.mouseCursorActive(xxcurrentView == 11);
4971 - desk.shortid = shortid;
4972 - desk.attemptWebRTC = attemptWebRTC;
4973 - desk.onStateChanged = onMultiDesktopStateChange;
4974 - //desk.onConsoleMessageChange = function () { console.log('CONSOLEMSG:', desk.consoleMessage); }
4975 - desk.m.ImageType = webpSupport?4:1; // Send 4 if WebP is supported, otherwise send 1 for JPEG.
4976 - desk.m.CompressionLevel = multidesktopsettings.quality;
4977 - desk.m.ScalingLevel = multidesktopsettings.scaling;
4978 - if (multidesktopsettings.framerate) { desk.m.FrameRateTimer = multidesktopsettings.framerate; }
4979 - if (multidesktopsettings.swapmouse) { desk.m.SwapMouse = multidesktopsettings.swapmouse; }
4980 - if (multidesktopsettings.rmw) { desk.m.ReverseMouseWheel = multidesktopsettings.rmw; }
4981 - if (multidesktopsettings.remotekeymap == true) { desk.m.remoteKeyMap = multidesktopsettings.remotekeymap; }
4982 - //desk.m.onDisplayinfo = deskDisplayInfo;
4983 - desk.m.onScreenSizeChange = mdeskAdjust; // Multi-Desktop Adjust
4984 - //if (debugmode > 0) { desk.m.onScreenSizeChange = mdeskAdjust; }
4985 - desk.Start(nodeid);
4986 - desk.contype = 1;
4987 - multiDesktop[nodeid] = desk;
4988 - }
4989 - } else {
4990 - // Disconnect and clean up the remote desktop
4991 - desk.Stop();
4992 - delete multiDesktop[nodeid];
4993 - }
4994 - }
4995 -
4996 - function getMeshActions(mesh, meshrights) {
4997 - if ((meshrights & 4) == 0) return '';
4998 - var r = '';
4999 - if (mesh.mtype == 1) {

This file is too large to show in full.