master
handlebars 20,674 lines 1.4 MB
Raw
Large file — syntax highlighting disabled.
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 rel="manifest" href="{{{domainurl}}}manifest.json">
11 <link type="image/x-icon" href="{{{domainurl}}}favicon.ico" rel="shortcut icon" />
12 <link keeplink=1 type="text/css" href="styles/style.css" media="screen" rel="stylesheet" title="CSS" />
13 <link type="text/css" href="styles/ol.css" media="screen" rel="stylesheet" title="CSS" />
14 <link type="text/css" href="styles/ol3-contextmenu.min.css" media="screen" rel="stylesheet" title="CSS" />
15 <link type="text/css" href="styles/xterm.css" media="screen" rel="stylesheet" title="CSS" />
16 <link type="text/css" href="styles/flatpickr.min.css" media="screen" rel="stylesheet" title="CSS">
17 {{{customCSSTags}}}
18 <link rel="apple-touch-icon" href="/favicon-303x303.png" />
19 <script type="text/javascript" src="scripts/common-0.0.1{{{min}}}.js"></script>
20 <script type="text/javascript" src="scripts/meshcentral{{{min}}}.js"></script>
21 <script type="text/javascript" src="scripts/amt-0.2.0{{{min}}}.js"></script>
22 <script type="text/javascript" src="scripts/amt-wsman-0.2.0{{{min}}}.js"></script>
23 <script type="text/javascript" src="scripts/amt-desktop-0.0.2{{{min}}}.js"></script>
24 <script type="text/javascript" src="scripts/amt-terminal-0.0.2{{{min}}}.js"></script>
25 <script type="text/javascript" src="scripts/zlib{{{min}}}.js"></script>
26 <script type="text/javascript" src="scripts/zlib-inflate{{{min}}}.js"></script>
27 <script type="text/javascript" src="scripts/zlib-adler32{{{min}}}.js"></script>
28 <script type="text/javascript" src="scripts/zlib-crc32{{{min}}}.js"></script>
29 <script type="text/javascript" src="scripts/amt-redir-ws-0.1.0{{{min}}}.js"></script>
30 <script type="text/javascript" src="scripts/amt-wsman-ws-0.2.0{{{min}}}.js"></script>
31 <script type="text/javascript" src="scripts/agent-redir-ws-0.1.1{{{min}}}.js"></script>
32 <script type="text/javascript" src="scripts/agent-redir-rtc-0.1.0{{{min}}}.js"></script>
33 <script type="text/javascript" src="scripts/agent-desktop-0.0.2{{{min}}}.js"></script>
34 <script type="text/javascript" src="scripts/agent-rdp-0.0.1{{{min}}}.js"></script>
35 <script type="text/javascript" src="scripts/qrcode.min.js"></script>
36 <script type="text/javascript" src="scripts/xterm{{{min}}}.js"></script>
37 <script type="text/javascript" src="scripts/xterm-addon-fit{{{min}}}.js"></script>
38 <script type="text/javascript" src="scripts/xterm-addon-image{{{min}}}.js"></script>
39 <script type="text/javascript" src="scripts/flatpickr.js"></script>
40 <script type="text/javascript" src="mstsc/mstsc.js"></script>
41 <script type="text/javascript" src="mstsc/keyboard.js"></script>
42 <script type="text/javascript" src="mstsc/rle.js"></script>
43 <script type="text/javascript" src="mstsc/client.js"></script>
44 <script type="text/javascript" src="mstsc/canvas.js"></script>
45 <script keeplink=1 type="text/javascript" src="scripts/u2f-api{{{min}}}.js"></script>
46 <script keeplink=1 type="text/javascript" src="scripts/charts{{{min}}}.js"></script>
47 <script keeplink=1 type="text/javascript" src="scripts/moment{{min}}.js"></script>
48 <script keeplink=1 type="text/javascript" src="scripts/chartjs-adapter-moment{{{min}}}.js"></script>
49 <script keeplink=1 type="text/javascript" src="scripts/filesaver.min.js"></script>
50 <script keeplink=1 type="text/javascript" src="scripts/ol{{{min}}}.js"></script>
51 <script keeplink=1 type="text/javascript" src="scripts/ol3-contextmenu{{{min}}}.js"></script>
52 <script keeplink=1 type="text/javascript" src="scripts/purify{{{min}}}.js"></script>
53 <script keeplink=1 type="text/javascript" src="scripts/marked{{{min}}}.js"></script>
54 {{{customJSTags}}}
55 <title>{{{title}}}</title>
56 </head>
57 <body id="body" oncontextmenu="handleContextMenu(event)" style="display:none;min-width:495px" onload="if (typeof(startup) !== 'undefined') startup();">
58 <!-- right click menu -->
59 <div id="contextMenu" class="contextMenu noselect" style="display:none">
60 <div id="cxinfo" class="cmtext" onclick="cmaction(1,event)"><b>Information</b></div>
61 <div id="cxdesktop" class="cmtext" onclick="cmaction(3,event)">Desktop</div>
62 <div id="cxterminal" class="cmtext" onclick="cmaction(2,event)">Terminal</div>
63 <div id="cxfiles" class="cmtext" onclick="cmaction(4,event)">Files</div>
64 <div id="cxevents" class="cmtext" onclick="cmaction(5,event)">Events</div>
65 <div id="cxdetails" class="cmtext" onclick="cmaction(6,event)">Details</div>
66 <div id="cxconsole" class="cmtext" onclick="cmaction(7,event)">Console</div>
67 <div id="cxwebrdp" class="cmtext" onclick="cmaction(11,event)">Web-RDP</div>
68 <div id="cxwebvnc" class="cmtext" onclick="cmaction(12,event)">Web-VNC</div>
69 <div id="cxwebssh" class="cmtext" onclick="cmaction(13,event)">Web-SSH</div>
70 <div id="cxrdp" class="cmtext" onclick="cmaction(14,event)">RDP</div>
71 <div id="cxplugins" class="cmtext" onclick="cmaction(8,event)" style="display:none">Plugins</div>
72 <hr id="cxmgroupsplit" />
73 <div id="cxstar" class="cmtext" onclick="cmaction(10,event)" style="display:none">Toggle Star</div>
74 <div id="cxmdesktop" class="cmtext" onclick="cmaction(9,event)" style="display:none">Multi-Desktop</div>
75 </div>
76 <div id="meshContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
77 <div id="cxselectall" class="cmtext" onclick="cmmeshaction(1,event)">Select All</div>
78 <div id="cxselectnone" class="cmtext" onclick="cmmeshaction(2,event)">Select None</div>
79 <!--
80 <hr id="cxmgroupsplit2" style="display:none" />
81 <div id="cxmmdesktop" class="cmtext" style="display:none" onclick="cmmeshaction(3,event)">Multi-Desktop</div>
82 -->
83 </div>
84 <div id="filesShellContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
85 <div class="cmtext" onclick="cmconnectfilesaction()">Ask Consent</div>
86 </div>
87 <div id="termShellContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
88 <div class="cmtext" onclick="cmtermaction(1,0,event)"><b>Admin Shell</b></div>
89 <div class="cmtext" onclick="cmtermaction(6,0,event)">Admin PowerShell</div>
90 <div class="cmtext" onclick="cmtermaction(8,0,event)">User Shell</div>
91 <div class="cmtext" onclick="cmtermaction(9,0,event)">User PowerShell</div>
92 <div class="cmtext" onclick="cmtermaction(1,0x10,event)">Ask Admin Shell</div>
93 <div class="cmtext" onclick="cmtermaction(6,0x10,event)">Ask Admin PowerShell</div>
94 <div class="cmtext" onclick="cmtermaction(8,0x10,event)">Ask User Shell</div>
95 <div class="cmtext" onclick="cmtermaction(9,0x10,event)">Ask User PowerShell</div>
96 </div>
97 <div id="termShellContextMenu2" class="contextMenu noselect" style="display:none;min-width:0px">
98 <div class="cmtext" onclick="cmtermaction(8,0,event)"><b>User Shell</b></div>
99 <div class="cmtext" onclick="cmtermaction(9,0,event)">User PowerShell</div>
100 </div>
101 <div id="termShellContextMenuLinux" class="contextMenu noselect" style="display:none;min-width:0px">
102 <div class="cmtext" onclick="cmtermaction(1,0,event)"><b>Root Shell</b></div>
103 <div class="cmtext" onclick="cmtermaction(8,0,event)">User Shell</div>
104 <div class="cmtext" onclick="cmtermaction(100,0,event)">Login Shell</div>
105 </div>
106 <div id="deskConnectContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
107 <div class="cmtext" onclick="cmdeskaction(1,event)">Ask Consent + Bar</div>
108 <div class="cmtext" onclick="cmdeskaction(2,event)">Ask Consent</div>
109 <div class="cmtext" onclick="cmdeskaction(3,event)">Privacy Bar</div>
110 </div>
111 <div id="deskDisconnectContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
112 <div class="cmtext" onclick="cmdeskaction(10,event)">Disconnect and Lock</div>
113 <div class="cmtext" onclick="cmdeskaction(11,event)">Disconnect</div>
114 </div>
115 <div id="altPortContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
116 <div class="cmtext" onclick="cmaltportaction(1,event)">Alternate Port</div>
117 </div>
118 <div id="sshPortContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
119 <div class="cmtext" onclick="cmsshportaction(1,event)">Alternate Port</div>
120 </div>
121 <div id="rfbPortContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
122 <div class="cmtext" onclick="cmrfbportaction(1,event)">Alternate Port</div>
123 </div>
124 <div id="httpPortContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
125 <div class="cmtext" onclick="cmhttpportaction(1,event)">Alternate Port</div>
126 </div>
127 <div id="httpsPortContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
128 <div class="cmtext" onclick="cmhttpsportaction(1,event)">Alternate Port</div>
129 </div>
130 <div id="filesContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
131 <div class="cmtext" onclick="cmfilesaction(1,event)">Rename</div>
132 <div class="cmtext" onclick="cmfilesaction(2,event)">Edit</div>
133 <div class="cmtext" onclick="cmfilesaction(3,event)">Delete</div>
134 </div>
135 <div id="expandAllContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
136 <div class="cmtext" onclick="cmexpandaction(1,event)">Expand All</div>
137 <div class="cmtext" onclick="cmexpandaction(2,event)">Collapse All</div>
138 </div>
139 <div id="deskPlayerContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
140 <div class="cmtext" onclick="cmdeskplayeraction(1,event)">Open Player...</div>
141 </div>
142 <div id="deskKeyShortcutContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
143 <div class="cmtext" onclick="cmdeskshortcutaction(1,event)">Customize...</div>
144 </div>
145 <div id="deskPreConfigShortcutContextMenu" class="contextMenu noselect" style="display:none;min-width:0px;max-height:calc(80% - 50px);overflow-y:auto">
146 <span id="deskPreConfigShortcutContextMenu1"></span>
147 <span id="deskPreConfigShortcutContextMenu2"></span>
148 <div class="cmtext" onclick="cmdeskpreconfigtypeaction(-1,event)">Customize...</div>
149 </div>
150 <div id="deskPreConfigScriptContextMenu" class="contextMenu noselect" style="display:none;min-width:0px;max-height:calc(80% - 50px);overflow-y:auto">
151 <span id="deskPreConfigScriptContextMenu1"></span>
152 <span id="deskPreConfigScriptContextMenu2"></span>
153 </div>
154 <!--
155 <div id="pluginTabContextMenu" class="contextMenu noselect" style="display:none;min-width:0px">
156 <div id="cxclose" class="cmtext" onclick="pluginTabClose(event)">Close Tab</div>
157 </div>
158 -->
159 <!-- main page -->
160 <div id=container>
161 <div id="notifiyBox" class="notifiyBox" style="display:none"></div>
162 <div id=masthead class=noselect>
163 <div style="float:left">{{{titlehtml}}}</div>
164 <div class="title" onclick="go(1,event)">{{{title1}}}</div>
165 <div class="title2">{{{title2}}}</div>
166 <div style="float:right">
167 <div id=notificationCount onclick="clickNotificationIcon()" class="unselectable" style="display: none;" title="Click to view current notifications">0</div>
168 </div>
169 <p id="logoutControl"><span id=logoutControlSpan class="logoncontrolspan"></span><span id=idleTimeoutNotify style="color:yellow"></span></p>
170 <div class=textnewui id=textnewui onmouseup=toggleBootstrapUIMode() onkeypress="if (event.key=='Enter') { toggleBootstrapUIMode(); }">
171 <b>Try the new MeshCentral UI</b>
172 </div>
173 </div>
174 <div id="page_leftbar">
175 <div style="height:16px"></div>
176 <div id=LeftMenuMyDevices tabindex=0 class="lbbutton lbbuttonsel" title="My Devices" onmouseup=go(1,event) onkeypress="if (event.key=='Enter') { go(1); }">
177 <div class="lbtg lb2"></div>
178 </div>
179 <div id=LeftMenuMyAccount tabindex=0 class="lbbutton" title="My Account" onmouseup=go(2,event) onkeypress="if (event.key=='Enter') { go(2); }">
180 <div class="lbtg lb1"></div>
181 </div>
182 <div id=LeftMenuMyEvents tabindex=0 class="lbbutton" title="My Events" onmouseup=go(3,event) onkeypress="if (event.key=='Enter') { go(3); }">
183 <div class="lbtg lb3"></div>
184 </div>
185 <div id=LeftMenuMyFiles tabindex=0 class="lbbutton" style="display:none" title="My Files" onmouseup=go(5,event) onkeypress="if (event.key=='Enter') { go(5); }">
186 <div class="lbtg lb4"></div>
187 </div>
188 <div id=LeftMenuMyUsers tabindex=0 class="lbbutton" style="display:none" title="My Users" onmouseup=go(4,event) onkeypress="if (event.key=='Enter') { go(4); }">
189 <div class="lbtg lb5"></div>
190 </div>
191 <div id=LeftMenuMyServer tabindex=0 class="lbbutton" style="display:none" title="My Server" onmouseup=go(6,event) onkeypress="if (event.key=='Enter') { go(6); }">
192 <div class="lbtg lb6"></div>
193 </div>
194 </div>
195 <div id=topbar class=noselect>
196 <div>
197 <div style="position:relative">
198 <span id=logoutControlSpan2></span>
199 <div tabindex=0 id=uiMenuButton title="User interface selection" onclick="showUserInterfaceSelectMenu()" onkeypress="if (event.key == 'Enter') showUserInterfaceSelectMenu()">
200 &diams;
201 <div id=uiMenu style="display:none">
202 <table>
203 <tr>
204 <td>
205 <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>
206 <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>
207 <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>
208 <div tabindex=0 id=uiViewButton7 class=uiSelector onclick=toggleBootstrapUIMode() title="Toggle Modern UI" onkeypress="if (event.key == 'Enter') toggleBootstrapUIMode()"><div class="uiSelector7"></div></div>
209 </td>
210 <td>
211 <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>
212 <div tabindex=0 id=uiViewButton4 class=uiSelector onclick=toggleNightMode() title="Toggle night mode" onkeypress="if (event.key == 'Enter') toggleNightMode()"><div class="uiSelector4"></div></div>
213 <div tabindex=0 id=uiViewButton5 class=uiSelector onclick=toggleFooterBarMode() title="Toggle footer bar" onkeypress="if (event.key == 'Enter') toggleFooterBarMode()"><div class="uiSelector5"></div></div>
214 <div class=uiSelector_end>&nbsp;</div>
215 </td>
216 </tr>
217 </table>
218 </div>
219 </div>
220 <table id=MainMenuSpan cellpadding=0 cellspacing=0 class=style1>
221 <tr>
222 <td tabindex=0 id=MainMenuMyDevices class="topbar_td style3x" onmouseup=go(1,event) onkeypress="if (event.key == 'Enter') go(1)">My Devices</td>
223 <td tabindex=0 id=MainMenuMyAccount class="topbar_td style3x" onmouseup=go(2,event) onkeypress="if (event.key == 'Enter') go(2)">My Account</td>
224 <td tabindex=0 id=MainMenuMyEvents class="topbar_td style3x" onmouseup=go(3,event) onkeypress="if (event.key == 'Enter') go(3)">My Events</td>
225 <td tabindex=0 id=MainMenuMyFiles class="topbar_td style3x" onmouseup=go(5,event) onkeypress="if (event.key == 'Enter') go(5)">My Files</td>
226 <td tabindex=0 id=MainMenuMyUsers class="topbar_td style3x" onmouseup=go(4,event) onkeypress="if (event.key == 'Enter') go(4)">My Users</td>
227 <td tabindex=0 id=MainMenuMyServer class="topbar_td style3x" onmouseup=go(6,event) onkeypress="if (event.key == 'Enter') go(6)">My Server</td>
228 <td class="topbar_td_end style3">&nbsp;</td>
229 </tr>
230 </table>
231 <div id=MainSubMenuSpan style="display:none">
232 <table id=MainSubMenu cellpadding=0 cellspacing=0 class=style1>
233 <tr>
234 <td tabindex=0 id=MainDev class="topbar_td style3x" onmouseup=go(10,event) onkeypress="if (event.key == 'Enter') go(10)">General</td>
235 <td tabindex=0 id=MainDevDesktop class="topbar_td style3x" onmouseup=go(11,event) onkeypress="if (event.key == 'Enter') go(11)">Desktop</td>
236 <td tabindex=0 id=MainDevTerminal class="topbar_td style3x" onmouseup=go(12,event) onkeypress="if (event.key == 'Enter') go(12)">Terminal</td>
237 <td tabindex=0 id=MainDevFiles class="topbar_td style3x" onmouseup=go(13,event) onkeypress="if (event.key == 'Enter') go(13)">Files</td>
238 <td tabindex=0 id=MainDevEvents class="topbar_td style3x" onmouseup=go(16,event) onkeypress="if (event.key == 'Enter') go(16)">Events</td>
239 <td tabindex=0 id=MainDevInfo class="topbar_td style3x" onmouseup=go(17,event) onkeypress="if (event.key == 'Enter') go(17)">Details</td>
240 <td tabindex=0 id=MainDevApps class="topbar_td style3x" onmouseup=go(18,event) onkeypress="if (event.key == 'Enter') go(18)">Software</td>
241 <td tabindex=0 id=MainDevAmt class="topbar_td style3x" onmouseup=go(14,event) onkeypress="if (event.key == 'Enter') go(14)">Intel&reg;AMT</td>
242 <td tabindex=0 id=MainDevConsole class="topbar_td style3x" onmouseup=go(15,event) onkeypress="if (event.key == 'Enter') go(15)">Console</td>
243 <td tabindex=0 id=MainDevPlugins class="topbar_td style3x" onmouseup=go(19,event) onkeypress="if (event.key == 'Enter') go(19)">Plugins</td>
244 <td class="topbar_td_end style3">&nbsp;</td>
245 </tr>
246 </table>
247 </div>
248 <div id=MeshSubMenuSpan style="display:none">
249 <table id=MeshSubMenu cellpadding=0 cellspacing=0 class=style1>
250 <tr>
251 <td tabindex=0 id=MeshGeneral class="topbar_td style3x" onmouseup=go(20,event) onkeypress="if (event.key == 'Enter') go(20)">General</td>
252 <td tabindex=0 id=MeshSummary class="topbar_td style3x" onmouseup=go(21,event) onkeypress="if (event.key == 'Enter') go(21)">Summary</td>
253 <td class="topbar_td_end style3">&nbsp;</td>
254 </tr>
255 </table>
256 </div>
257 <div id=EventsSubMenuSpan style="display:none">
258 <table id=EventsSubMenu cellpadding=0 cellspacing=0 class=style1>
259 <tr>
260 <td tabindex=0 id=EventsLive class="topbar_td style3x" onmouseup=go(3,event) onkeypress="if (event.key == 'Enter') go(3)">Events</td>
261 <td tabindex=0 id=EventsReport class="topbar_td style3x" onmouseup=go(60,event) onkeypress="if (event.key == 'Enter') go(60)">Reports</td>
262 <td class="topbar_td_end style3">&nbsp;</td>
263 </tr>
264 </table>
265 </div>
266 <div id=UserSubMenuSpan style="display:none">
267 <table id=UserSubMenu cellpadding=0 cellspacing=0 class=style1>
268 <tr>
269 <td tabindex=0 id=UserGeneral class="topbar_td style3x" onmouseup=go(30,event) onkeypress="if (event.key == 'Enter') go(30)">General</td>
270 <td tabindex=0 id=UserEvents class="topbar_td style3x" onmouseup=go(31,event) onkeypress="if (event.key == 'Enter') go(31)">Events</td>
271 <td class="topbar_td_end style3">&nbsp;</td>
272 </tr>
273 </table>
274 </div>
275 <div id=UsersSubMenuSpan style="display:none">
276 <table id=UsersSubMenu cellpadding=0 cellspacing=0 class=style1>
277 <tr>
278 <td tabindex=0 id=UsersGeneral class="topbar_td style3x" onmouseup=go(4,event) onkeypress="if (event.key == 'Enter') go(4)">Users</td>
279 <td tabindex=0 id=UsersGroups class="topbar_td style3x" onmouseup=go(50,event) onkeypress="if (event.key == 'Enter') go(50)">Groups</td>
280 <td tabindex=0 id=UsersRecordings class="topbar_td style3x" onmouseup=go(52,event) onkeypress="if (event.key == 'Enter') go(52)">Recordings</td>
281 <td class="topbar_td_end style3">&nbsp;</td>
282 </tr>
283 </table>
284 </div>
285 <div id=ServerSubMenuSpan style="display:none">
286 <table id=ServerSubMenu cellpadding=0 cellspacing=0 class=style1>
287 <tr>
288 <td tabindex=0 id=ServerGeneral class="topbar_td style3x" onmouseup=go(6,event) onkeypress="if (event.key == 'Enter') go(6)">General</td>
289 <td tabindex=0 id=ServerStats class="topbar_td style3x" onmouseup=go(40,event) onkeypress="if (event.key == 'Enter') go(40)">Stats</td>
290 <td tabindex=0 id=ServerConsole class="topbar_td style3x" onmouseup=go(115,event) onkeypress="if (event.key == 'Enter') go(115)">Console</td>
291 <td tabindex=0 id=ServerTrace class="topbar_td style3x" onmouseup=go(41,event) onkeypress="if (event.key == 'Enter') go(41)">Trace</td>
292 <td tabindex=0 id=ServerPlugins class="topbar_td style3x" onmouseup=go(42,event) onkeypress="if (event.key == 'Enter') go(42)">Plugins</td>
293 <td class="topbar_td_end style3">&nbsp;</td>
294 </tr>
295 </table>
296 </div>
297 <div id=UserDummyMenuSpan>
298 <table id=UserDummyMenu cellpadding=0 cellspacing=0 class=style1>
299 <tr><td class=style3 style="">&nbsp;</td></tr>
300 </table>
301 </div>
302 </div>
303 </div>
304 </div>
305 <div id="column_l">
306 <div id=p0 style="display:none">
307 <div id=p0message><span id=p0span>Server disconnected</span>, <href onclick=reload() style=cursor:pointer><u>click to reconnect</u></href>.</div>
308 </div>
309 <div id=p1 style="display:none">
310 <div id="p1title">
311 <div style="display:none" id="devListToolbarViewIcons">
312 <div id=devViewPageState style="float:left;line-height:32px;height:32px;font-size:16px;display:none"></div>
313 <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>
314 <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>
315 <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>
316 <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>
317 <div tabindex=0 id=devViewButton1 class=viewSelector onclick=onDeviceViewChange(1) onkeypress="if (event.key == 'Enter') { onDeviceViewChange(1); }" title="Columns"><div class="viewSelector2"></div></div>
318 <div tabindex=0 id=devViewButton2 class=viewSelector onclick=onDeviceViewChange(2) onkeypress="if (event.key == 'Enter') { onDeviceViewChange(2); }" title="List"><div class="viewSelector1"></div></div>
319 <div tabindex=0 id=devViewButton3 class=viewSelector onclick=onDeviceViewChange(3) onkeypress="if (event.key == 'Enter') { onDeviceViewChange(3); }" title="Desktops"><div class="viewSelector3"></div></div>
320 <div tabindex=0 id=devViewButton5 class=viewSelector onclick=onDeviceViewChange(5) onkeypress="if (event.key == 'Enter') { onDeviceViewChange(5); }" title="Desktops"><div class="viewSelector7"></div></div>
321 <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>
322 </div><div><h1>My Devices</h1></div>
323 </div>
324 <table id="devListToolbarSpan" class="noselect">
325 <tr>
326 <td class=h1></td>
327 <td id=devListToolbar class=style14 style="display:none">
328 <button type="button" title="Collapse All" id="CollapseAllButton" onclick="cmexpandaction(2)"><img src="images/icon-collapse.png" width="10" height="10"></button>
329 <button type="button" title="Expand All" id="ExpandAllButton" onclick="cmexpandaction(1)"><img src="images/icon-expand.png" width="10" height="10"></button>
330 <input type=button id=SelectAllButton onclick="selectallButtonFunction();" value="Select All" />&nbsp;
331 <input type=button id=GroupActionButton disabled="disabled" value="Group Action" onclick=groupActionFunction() />&nbsp;
332 <input type=button id=ScrollToTopButton onclick="onDevicesScroll(true);" style="display:none;margin-right:4px" value="Scroll To Top" />
333 <input id=SearchInput type=input autocomplete=off placeholder=Filter onchange=onDeviceSearchChanged(event) onclick=onDeviceSearchChanged(event) onkeyup=onDeviceSearchChanged(event) onfocus=onSearchFocus(1) onblur=onSearchFocus(0) title="Filter devices by name or prefix: user: (u:) ip: group: (g:) tag: (t:) atag: (a:) os: amt: desc: wsc:ok/noav/noupdate/nofirewall/any connectivity: (c:) - Prefix with ! to negate, e.g. !tag:abc"/>&nbsp;
334 <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>
335 <select id=DevFilterSelect onchange=onOnlineCheckBox(event) title="Device Filter">
336 <option value=0>All</option>
337 <option value=1>Online</option>
338 <option value=5>Offline</option>
339 <option value=2>Sessions</option>
340 <option value=3>Starred</option>
341 <option value=4>Intel&reg; AMT</option>
342 <option value=6>Help</option>
343 <option value=7>Tagged</option>
344 <option value=8>Untagged</option>
345 </select>
346 <label><input type=checkbox id=RealNameCheckBox onclick=onRealNameCheckBox() /><span title="Show devices operating system name">OS Name</span></label>
347 <label style="display:none"><input type=checkbox id=OnlineCheckBox onclick=onOnlineCheckBox(event) /><span title="Only show devices that are online">Online</span></label>
348 <span id="devsCustomUIBar"></span>
349 </td>
350 <td id=kvmListToolbar class=style14 style="display:none">
351 <button type="button" title="Collapse All" id="CollapseAllButton2" onclick="cmexpandaction(2)"><img src="images/icon-collapse.png" width="10" height="10"></button>
352 <button type="button" title="Expand All" id="ExpandAllButton2" onclick="cmexpandaction(1)"><img src="images/icon-expand.png" width="10" height="10"></button>
353 <span id=KvmDesktopButtons>
354 <input type="button" onclick="connectAllKvmFunction()" value="Connect All" />&nbsp;
355 <input type="button" onclick="disconnectAllKvmFunction()" value="Disconnect All" />&nbsp;
356 <label><input type="checkbox" id="autoConnectDesktopCheckbox" onclick="autoConnectDesktops(event)" title="Automatic connect" />Auto&nbsp;</label>
357 </span>
358 <input type="button" onclick="onDevicesScroll(true);" value="Scroll To Top" />&nbsp;
359 <input type="button" onclick="showMultiDesktopSettings()" value="Settings" />&nbsp;
360 <input id=KvmSearchInput type=search placeholder=Filter onchange=onDeviceSearchChanged(event) onclick=onDeviceSearchChanged(event) onkeyup=onDeviceSearchChanged(event) autocomplete=off onfocus=onSearchFocus(1) onblur=onSearchFocus(0) title="Filter devices by name or prefix: user: (u:) ip: group: (g:) tag: (t:) atag: (a:) os: amt: desc: wsc:ok/noav/noupdate/nofirewall/any connectivity: (c:) - Prefix with ! to negate, e.g. !tag:abc" />&nbsp;
361 <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>
362 </td>
363 <td id=devMapToolbar class=style14 style="display:none">
364 &nbsp;&nbsp;<input type=search id=mapSearchLocation placeholder="Search Location" onfocus=onMapSearchFocus(1) onblur=onMapSearchFocus(0) />
365 <input type=button value=Search title="Search for location" onclick=getSearchLocation() />
366 <input type=button id=refreshmap title="Reset map view" value=Reset onclick=refreshMap(false,true) />
367 </td>
368 <td class="auto-style1" style=height:100%>
369 <img style="display:none;cursor:pointer;margin-top:3px" id=devListToolbarSettings src="images/icon-gear.png" width=16 height=16 onclick="onDeviceViewSettings()" />
370 <div style="display:none" id=devListToolbarView>
371 View
372 <select id=viewselect onchange=onDeviceViewChange()>
373 <option value=1>Columns</option>
374 <option value=2>List</option>
375 <option value=3>Desktops Fixed Width</option>
376 <option id=viewselectmapoption value=4 style="display:none">Map</option>
377 <option value=5>Desktops</option>
378 </select>
379 </div>
380 <div style="display:none" id=devListToolbarSort>
381 Sort
382 <select id=sortselect onchange=mainUpdate(6)>
383 <option>Group</option>
384 <option>Power</option>
385 <option>Device</option>
386 <option>Tags</option>
387 <option>Group-Tags</option>
388 <option>Last Seen</option>
389 <option>Last Boot Up Time</option>
390 </select>
391 &nbsp;
392 </div>
393 <div style="display:none" id=devListToolbarSize>
394 Size
395 <select id=sizeselect onchange=onDeviceViewChange()>
396 <option value=0>Small</option>
397 <option value=1>Medium</option>
398 <option value=2>Large</option>
399 </select>
400 &nbsp;
401 </div>
402 </td>
403 <td class=h2></td>
404 </tr>
405 </table>
406 <div id=NoMeshesPanel style="display:none">
407 <table>
408 <tr>
409 <td valign="top" style="width:50px">
410 <img src="images/info.png" width="47" height="48" />
411 </td>
412 <td>
413 <div id="getStarted1">To get started, <a href=# onclick="return account_createMesh()"><strong>click here to create a device group</strong></a>.</div>
414 <div id="getStarted2">No device groups.</div>
415 </td>
416 </tr>
417 </table>
418 </div>
419 <div id="xdevices" class="noselect" style="display:none" onscroll="onDevicesScroll(false)"></div>
420 <div id="xdevicesmap" style="display:none">
421 <div id=xmapSearchResultsDlg style="display:none">
422 <div id=xmapSearchResultsBck>
423 <div id=xmapSearchClose onclick=mapCloseSearchWindow()><b>X</b></div>
424 <div style=padding:5px>Location Results</div>
425 <div style=width:100%;margin:6px></div>
426 </div>
427 <div id=xmapSearchResults style=margin:6px></div>
428 </div>
429 </div>
430 <div id="xmap-info-window"></div>
431 </div>
432 <div id=p2 style="display:none">
433 <div id="p2title"><h1>My Account</h1></div>
434 <div id="p2info" style="overflow-y:auto">
435 <div id="p2AccountImageFrame">
436 <img id="p2AccountImage" alt="" width="128" height="128" onclick="account_manageImage(0)" src="images/user-256.png" />
437 <div><a onclick="account_manageImage(0)">Change image</a></div>
438 </div>
439 <div id="p2AccountSecurity" style="display:none">
440 <p><strong>Account security</strong></p>
441 <div style="margin-left:25px">
442 <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>
443 <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>
444 <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>
445 <div id="manageDuoApp"><div class="p2AccountActions"><span id="authDuoSetupCheck"><strong>&#x2713;</strong></span></div><span><a href=# onclick="return account_manageAuthDuo()">Manage Duo authentication</a><br /></span></div>
446 <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>
447 <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>
448 <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>
449 <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>
450 </div>
451 </div>
452 <div id="p2AccountActions">
453 <p><strong>Account actions</strong></p>
454 <p class="mL">
455 <span id="viewPreviousLogins"><a href=# onclick="return account_viewPreviousLogins()">View previous logins</a><br /></span>
456 <span id="managePhoneNumber2" style="display:none"><a href=# onclick="return account_managePhone()">Manage phone number</a><br /></span>
457 <span id="manageMessaging2" style="display:none"><a href=# onclick="return account_manageMessaging()">Manage messaging</a><br /></span>
458 <span id="verifyEmailId" style="display:none"><a href=# onclick="return account_showVerifyEmail()">Verify email</a><br /></span>
459 <span id="accountEnableNotificationsSpan" style="display:none"><a href=# onclick="return account_enableNotifications()">Enable web notifications</a><br /></span>
460 <a href=# onclick="return account_showLocalizationSettings()">Localization Settings</a><br />
461 <a href=# onclick="return account_showAccountNotifySettings()">Notification Settings</a><br />
462 <span id="p2AccountPassActions">
463 <span id="accountChangeEmailAddressSpan" style="display:none"><a href=# onclick="return account_showChangeEmail()">Change email address</a><br /></span>
464 <a href=# onclick="return account_showChangePassword()">Change password</a><span id="p2nextPasswordUpdateTime"></span><br />
465 <a href=# onclick="return account_showDeleteAccount()">Delete account</a><br />
466 </span>
467 <span id="accountCreateLoginTokenSpan" style="display:none"><a href=# onclick="return account_createLoginToken()">Create login token</a><br /></span>
468 </p>
469 <br style=clear:both />
470 </div>
471 <div id=p2logintokens></div>
472 <strong>Device Groups</strong>
473 <span id="p2createMeshLink1"> - <a href=# onclick="return account_createMesh()" class="newMeshBtn"> New</a></span>
474 <br /><br />
475 <div id=p2meshes></div>
476 <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>
477 <br style=clear:both />
478 </div>
479 </div>
480 <div id=p3 style="display:none">
481 <div id="p3title"><h1>My Events</h1></div>
482 <table class="pTable">
483 <tr>
484 <td class="h1"></td>
485 <td class="auto-style1">
486 <input type="button" style="display:none" value="Download Report" onclick=p3showReportDialog() />
487 </td>
488 <td class="auto-style1">
489 Filter
490 <select id=p3filterevents onchange=refreshEvents()>
491 <option notransval=1 value="">All Logs</option>
492 <option notransval=1 value=agentlog>Agent Logs</option>
493 <option notransval=1 value=relaylog>Relay Logs</option>
494 <option notransval=1 value=manual>Manual Logs</option>
495 <option notransval=1 value=runcommands>Run Command Logs</option>
496 <option notransval=1 value=batchupload>Batch Upload Logs</option>
497 <option notransval=1 value=changenode>Change Node Logs</option>
498 <option notransval=1 value=removenode>Remove Node Logs</option>
499 </select>
500 Show
501 <select id=p3limitdropdown onchange=refreshEvents()>
502 <option notransval=1 value=60>Last 60</option>
503 <option notransval=1 value=120>Last 120</option>
504 <option notransval=1 value=250>Last 250</option>
505 <option notransval=1 value=500>Last 500</option>
506 <option notransval=1 value=1000>Last 1000</option>
507 <option notransval=1 value="">No limit</option>
508 </select>&nbsp;
509 <a href=# onclick=p3showDownloadEventsDialog(2)><img src=images/link4.png height=10 width=10 title="Download Events" style=cursor:pointer></a>&nbsp;
510 </td>
511 <td class="h2"></td>
512 </tr>
513 </table>
514 <div id=p3events></div>
515 </div>
516 <div id=p4 style="display:none">
517 <div id="p4title"><h1>My Users</h1></div>
518 <table class="pTable">
519 <tr>
520 <td class="h1"></td>
521 <td class="style14">
522 <div style="float:right">
523 <input type=button onclick=showUserBroadcastDialog() style=margin-right:6px value="Broadcast" />
524 <a href=# onclick=p4downloadUserInfo()><img style="cursor:pointer" title="Download user information" src="images/link4.png" /></a>
525 <a href=# onclick=p4batchAccountCreate()><img id=p4UserBatchCreate style="cursor:pointer;display:none" title="Batch create many user accounts" src="images/link6.png" /></a>
526 <img style="cursor:pointer;margin-top:3px;margin-left:6px" id=usersListToolbarSettings src="images/icon-gear.png" width=16 height=16 onclick="onUsersViewSettings()" />
527 </div>
528 <div>
529 <input type=button id=UsersSelectAllButton onclick="p3usersSelectallButtonFunction()" value="Select All" />
530 <input type=button id=UsersGroupActionButton disabled="disabled" value="Group Action" onclick=p3usersGroupActionFunction() />
531 <input id=UserNewAccountButton type=button onclick=showCreateNewAccountDialog() value="New Account..." />
532 <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) />
533 </div>
534 </td>
535 <td class="h2"></td>
536 </tr>
537 </table>
538 <div id="p3users"></div>
539 </div>
540 <div id=p5 style="display:none">
541 <div id="p5title"><h1>My Files</h1></div>
542 <table id="p5toolbar" cellpadding="0" cellspacing="0">
543 <tr>
544 <td id="p5filehead" valign=bottom>
545 <div id="p5rightOfButtons"></div>
546 <div>
547 <input type=button id=p5FolderUp disabled="disabled" onclick="return p5folderup();" value="Up" />&nbsp;
548 <input type=button id=p5SelectAllButton disabled="disabled" onclick="p5selectallfile();" value="Select All" />&nbsp;
549 <input type=button id=p5RenameFileButton disabled="disabled" value="Rename" onclick="p5renamefile();" />&nbsp;
550 <input type=button id=p5DeleteFileButton disabled="disabled" value="Delete" onclick="p5deletefile();" />&nbsp;
551 <input type=button id=p5ViewFileButton disabled="disabled" value="Edit" onclick="p5viewfile()" />&nbsp;
552 <input type=button id=p5NewFolderButton disabled="disabled" value="New Folder" onclick="p5createfolder();" />&nbsp;
553 <input type=button id=p5UploadButton disabled="disabled" value="Upload" onclick="p5uploadFile()" />&nbsp;
554 <input type=button id=p5DownloadButton disabled="disabled" value="Download" onclick="p5downloadButton()" />&nbsp;
555 <input type=button id=p5CutButton disabled="disabled" value="Cut" onclick="p5copyFile(1)" />&nbsp;
556 <input type=button id=p5CopyButton disabled="disabled" value="Copy" onclick="p5copyFile(0)" />&nbsp;
557 <input type=button id=p5PasteButton disabled="disabled" value="Paste" onclick="p5pasteFile()" />&nbsp;
558 </div>
559 </td>
560 </tr>
561 <tr>
562 <td id="p5filesubhead">
563 <div style=float:right>
564 <select id=p5sortdropdown onchange=updateFiles()>
565 <option value="1" selected="selected">Sort by name</option>
566 <option value="2">Sort by size</option>
567 <option value="3">Sort by date</option>
568 <option value="4">Descend by name</option>
569 <option value="5">Descend by size</option>
570 <option value="6">Descend by date</option>
571 </select>
572 </div>
573 <div>&nbsp;&nbsp;<span id="p5currentpath"></span></div>
574 </td>
575 </tr>
576 </table>
577 <div id="p5filetable">
578 <!--
579 <form id=p5fileCatchAll method=post enctype=multipart/form-data action=uploadfile.ashx target=fileUploadFrame>
580 <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)" />
581 <input id=p5fileDragLink2 name="link" style="display:none" />
582 <input type=submit id=p5fileCatchAllSubmit style="display:none" />
583 </form>
584 -->
585 <div id="p5PublicShare" style=""><div>These files are shared publicly, click "link" to get public url.</div></div>
586 <div id="bigok" style="display:none"><b>&checkmark;</b></div>
587 <div id="bigfail" style="display:none"><b>&#10007;</b></div>
588 <span id="p5files"></span>
589 </div>
590 <table id="p5toolbarBottom" style=width:100% cellpadding=0 cellspacing=0>
591 <tr><td class=style6>&nbsp;<span id="p5bottomstatus"></span></td></tr>
592 </table>
593 </div>
594 <div id=p6 style="display:none">
595 <div id="p6info" style="overflow-y:auto">
596 <div id="p6title">
597 <img id=MainMeshImage src="serverpic.ashx">
598 <h1>My Server</h1>
599 </div>
600 <div id="p2ServerActions">
601 <p><strong>Server actions</strong></p>
602 <div style="margin-left:25px">
603 <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>
604 <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>
605 <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>
606 <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>
607 <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>
608 <div id="p2ServerActionsConfig"><div class="p2AccountActions"><span style="display:none"><strong>&#x2713;</strong></span></div><a href=# onclick="return server_showConfigDlg()">Show server configuration</a></div>
609 </div>
610 <br />
611 </div>
612 <strong>Server Statistics</strong><br /><br />
613 <div id="serverStats">
614 <div id="serverCpuChartView" style="display:none">
615 <div class="chartViewCanvas" style="height:40px; text-align:center"><canvas id="serverCpuChart" style="display:inline-block"></canvas></div>
616 <div class="chartViewText" id="serverCpuChartText"></div>
617 </div>
618 <div id="serverMemoryChartView" style="display:none">
619 <div class="chartViewCanvas" style="height:40px; text-align:center"><canvas id="serverMemoryChart" style="display:inline-block"></canvas></div>
620 <div class="chartViewText" id="serverMemoryChartText"></div>
621 </div><br /><br />
622 <div id="serverStatsTable"></div>
623 </div>
624 <div id="serverWarningsDiv" style="display:none">
625 <br /><strong>Server Warnings</strong><br /><br />
626 <div id="serverCertWarnings"></div>
627 <div id="serverWarnings"></div>
628 </div>
629 </div>
630 </div>
631 <div id=p10 style="display:none">
632 <div id=p10title>
633 <div id="p10BackButton"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
634 <h1>General - <span id=p10deviceName></span></h1>
635 </div>
636 <div id=p10info style="overflow-y:auto">
637 <table style="width:100%" cellpadding="0" cellspacing="0">
638 <tr>
639 <td style=width:auto valign=top>
640 <div id=p10html></div>
641 </td>
642 <td style=width:100px;display:none id=notesPanel valign=top>
643 <table>
644 <thead>
645 <tr><th>Notes</th></tr>
646 </thead>
647 <tbody>
648 <tr>
649 <td><div id=notesPanelArea style=width:300px;height:200px;resize:none;overflow-y:scroll></div></td>
650 </tr>
651 </tbody>
652 </table>
653 </td>
654 <td style=width:20px></td>
655 <td style=width:200px;vertical-align:top;position:relative valign=top>
656 <div class="deviceNotifyLargeDot">
657 <div id="p10deviceMsg" onclick=showDeviceMessages(null,null,event) class=deviceNotifyDotSub></div>
658 <img id="p10deviceStar" class=deviceNotifyDotSub src=images/icon-star-notify-16.png width=16 height=16>
659 <img id="p10deviceNotify" onclick=showDeviceSessions(null,null,event) class=deviceNotifyDotSub src=images/icon-relay-notify.png width=16 height=16>
660 <img id="p10deviceHelp" onclick=showDeviceHelpRequests(null,null,event) class=deviceNotifyDotSub src=images/icon-help-notify-16.png width=16 height=16>
661 </div>
662 <div id="p10deviceBattery" class="deviceBatteryLarge deviceBatteryLarge1"></div>
663 <a href=# onclick=p10showiconselector()><img id=MainComputerImage></a>
664 <div id=MainComputerState></div>
665 </td>
666 </tr>
667 </table><br />
668 <div id=p10html2></div>
669 <div id=p10html3></div>
670 <div id=p10html4></div>
671 <div id=p10html5></div>
672 </div>
673 </div>
674 <div id=p11 class="noselect" style="display:none">
675 <div id="p11title">
676 <div id=p11deviceNameHeader>
677 <div id="p11BackButton"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
678 <div id="devListToolbarViewIcons"><div class="viewSelector" onclick=deskToggleFull(event) title="Full Screen. Hold shift to browser full screen."><div class="viewSelector5"></div></div></div>
679 <h1>Desktop - <span id=p11deviceName></span></h1>
680 </div>
681 </div>
682 <div id="p11warning" onclick="showFeaturesDlg()">
683 <div class="icon2"></div>
684 <div class="warningbox">Redirection port or KVM feature is disabled<span id="p11warninga">, click here to enable it.</span></div>
685 </div>
686 <div id="p11warning2" onclick="showPowerActionDlg()">
687 <div class="icon2"></div>
688 <div class="warningbox">Remote computer is not powered on, click here to issue a power command.</div>
689 </div>
690 <div style="position:absolute;right:16px;margin-top:-14px;font-size:x-small;color:black">
691 <div id="p11capslock" style="display:inline-block;margin-left:1px;border-radius:5px;background-color:#A3FFB8;padding:2px">
692 CAPS
693 </div>
694 <div id="p11scrolllock" style="display:inline-block;margin-left:1px;border-radius:5px;background-color:#A3FFB8;padding:2px">
695 SCROLL
696 </div>
697 <div id="p11numlock" style="display:inline-block;margin-left:1px;border-radius:5px;background-color:#A3FFB8;padding:2px">
698 NUM
699 </div>
700 </div>
701 <div id=deskarea0 cellpadding=0 cellspacing=0>
702 <div id=deskarea1 class="areaHead">
703 <div class="toright2">
704 <div id="idx_deskFullBtn2" onclick=deskToggleFull(event) style="float:right">&nbsp;&#x2716;</div>
705 <span id="p11power"></span>&nbsp;
706 <div class='deskareaicon' title="Toggle View Mode" onclick="toggleAspectRatio(1)">&#8690;</div>
707 <div class='deskareaicon' title="Rotate Left" onclick="drotate(-1)">&olarr;</div>
708 <div class='deskareaicon' title="Rotate Right" onclick="drotate(1)">&orarr;</div>
709 <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>
710 <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" />
711 <input id="deskActionsBtn" type=button title="Perform power actions on the device" onkeypress="return false" onkeydown="return false" value=Actions onclick=deviceActionFunction() class="mR" />
712 <input id="deskActionsSettings" type="button" value="Settings..." title="Edit remote desktop settings" onkeypress="return false" onkeydown="return false" onclick="showDesktopSettings()" class="mR" />
713 <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" />
714 <div id="desktopCustomUpperRight" style="float:left;margin-right:6px"></div>
715 <div id="desktopCustomUiButtons" style="float:left"></div>
716 </div>
717 <div>
718 <input type="button" id="autoconnectbutton1" value="AutoConnect" onclick=autoConnectDesktop(event) onkeypress="return false" onkeydown="return false" style="display:none;margin-right:4px" />
719 <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>
720 <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>
721 <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>
722 <span id=disconnectbutton1span><input type=button id=disconnectbutton1 cmenu="deskDisconnectButton" value="Disconnect" onclick=connectDesktop(event,0) onkeypress="return false" onkeydown="return false" /></span>
723 <span id="deskstatus" style="line-height:22px">Disconnected</span><span id="deskmetadata"></span>
724 </div>
725 </div>
726 <div id=deskarea3x>
727 <div id="p11bigok" style="display:none"><b>&checkmark;</b></div>
728 <div id="p11bigfail" style="display:none"><b>&#10007;</b></div>
729 <div id=DeskFocus oncontextmenu="return false" onmousedown=dmousedown(event) onmouseup=dmouseup(event) onmousemove=dmousemove(event)></div>
730 <div id=DeskParent>
731 <canvas id=Desk width=640 height=480 oncontextmenu="return false" onmousedown=dmousedown(event) onmouseup=dmouseup(event) onmousemove=dmousemove(event) onmousewheel=dmousewheel(event)></canvas>
732 </div>
733 <div id=DeskTools>
734 <div id=deskToolsAreaTop>
735 <a id=DeskToolsRefreshButton style="right:2px" onclick="refreshDeskTools()">Refresh</a>
736 <div id=deskToolsTopTabProcess class="deskToolsTopTab" onclick="changeDeskToolTab(0)" style="left:0px;bottom:0px">Processes</div>
737 <div id=deskToolsTopTabService class="deskToolsTopTab" onclick="changeDeskToolTab(1)" style="display:none;left:90px;color:gray">Services</div>
738 </div>
739 <div id=deskToolsArea>
740 <div id="DeskToolsProcessTab">
741 <div id=deskToolsHeader>
742 <a class="colmn1" title="Sort by process id" onclick=sortProcess(0)>PID</a>
743 <a class="colmn2" title="Sort by name" onclick=sortProcess(1)>Name</a>
744 </div>
745 <div id="DeskToolsProcesses" style=""></div>
746 </div>
747 <div id="DeskToolsServiceTab" style="display:none">
748 <div id=deskToolsServiceHeader>
749 <a class="colmn1" style="width:70px" title="Sort by state" onclick=sortService(0)>State</a>
750 <a class="colmn2" title="Sort by name" onclick=sortService(1)>Name</a>
751 </div>
752 <div id="DeskToolsServices" style=""></div>
753 </div>
754 </div>
755 </div>
756 <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>
757 <div id=p11DeskSessionSelector style="display:none;position:absolute;left:30px;top:17px;right:30px;bottom:17px;overflow-y:auto"></div>
758 </div>
759 <div id=deskarea4 class="areaFoot">
760 <div class="toright2">
761 <span id="DeskLatency" style="line-height:22px;width:50px" title="Desktop Session Latency"></span>
762 <span id="DeskTimer" style="line-height:22px" title="Session time"></span>
763 <input id=DeskToolsButton type=button value=Tools title="Toggle tools view" onkeypress="return false" onkeydown="return false" onclick="toggleDeskTools()" />
764 <span>&nbsp;</span>
765 <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>
766 <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>
767 <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>
768 <span id=DeskLockButton title="Lock the remote computer"><img src='images/icon-lock.png' class="desktopButtons" onclick=deviceLockFunction() height=16 width=16 /></span>
769 <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>
770 <span id=DeskBackgroundButton title="Toggle remote desktop background"><img class="desktopButtons" id=DeskBackgroundButtonImage src='images/icon-background.png' onclick=deviceToggleBackground(event) height=16 width=16 /></span>
771 <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>
772 <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>
773 <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>
774 <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>
775 <span id=DeskRefreshButton title="Refresh the desktop"><img class="desktopButtons" id=DeskRefreshButtonImage src='images/icon-refresh.png' onclick=deskRefreshFunction() height=16 width=16 /></span>
776 <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>
777 <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>
778 <span id=DeskGuestShareButton title="Share the device with a guest"><img class="desktopButtons" id=DeskGuestShareButtonImage src='images/icon-share2.png' onclick=showShareDevice() height=16 width=16 /></span>
779 </div>
780 <div class="toright2"><span id=DeskMonitorSelectionSpan></span></div>
781 <div>
782 <select id="deskkeys" cmenu=deskKeyShortcutContextMenu></select>
783 <input id="DeskWD" type=button value="Send" onkeypress="return false" onkeydown="return false" onclick="deskSendKeys()" />
784 <input id="DeskESC" style="display:none" type="button" value="ESC" onkeypress="return false" onkeydown="return false" onclick="sendDeskEsc()" />
785 <input id="DeskClip" type="button" value="Clipboard" onkeypress="return false" onkeydown="return false" onclick="showDeskClip()" />
786 <input id="DeskType" cmenu="deskPreConfigShortcutContextMenu" type="button" value="Type" onkeypress="return false" onkeydown="return false" onclick="showDeskType()" />
787 <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;
788 </div>
789 </div>
790 </div>
791 </div>
792 <div id=p12 style="display:none">
793 <div id="p12title">
794 <div id="p12BackButton"><div class="backButton" tabindex=0 onclick=goBack() title="Back" onkeypress="if (event.key == 'Enter') goBack()"><div class="backButtonEx"></div></div></div>
795 <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>
796 <h1>Terminal - <span id=p12deviceName></span></h1>
797 </div>
798 <div id="p12warning" onclick=showFeaturesDlg()>
799 <div class="icon2"></div>
800 <div class="warningbox">Redirection port or KVM feature is disabled<span id="p12warninga">, click here to enable it.</span></div>
801 </div>
802 <div id="p12warning2" onclick=showPowerActionDlg()>
803 <div class="icon2"></div>
804 <div class="warningbox">Remote computer is not powered on, click here to issue a power command.</div>
805 </div>
806 <div id=termTable style="position:relative">
807 <table style="width:100%" cellpadding=0 cellspacing=0>
808 <tr>
809 <td class="areaHead" style="line-height:22px">
810 <div class="toright2">
811 <div id="idx_termFullBtn2" onclick=deskToggleFull(event) style="float:right">&nbsp;&#x2716;</div>
812 <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>
813 <input id="termActionsBtn" type=button title="Perform power actions on the device" onkeypress="return false" onkeydown="return false" value=Actions onclick=deviceActionFunction() />
814 <div id="terminalCustomUpperRight" style="float:left;margin-right:6px"></div>
815 <div id="terminalCustomUiButtons" style="float:left"></div>
816 </div>
817 <div>
818 <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>
819 <span id="termstatus" style="line-height:22px">Disconnected</span><span id="termtitle"></span>
820 </div>
821 </td>
822 </tr>
823 <tr>
824 <td id="termarea3x">
825 <div id="termarea3xdiv" style="width:100%;height:100%;text-align:left"></div>
826 <pre id="Term"></pre>
827 </td>
828 </tr>
829 <tr>
830 <td class="areaFoot">
831 <div class="toright2">
832 <span id="TermTimer" title="Session time"></span>&nbsp;
833 <span id="terminalSettingsButtons" style="display:none">
834 <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()" />
835 <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()" />
836 <input id="id_ttypebutton" type="button" onkeypress="return false" onkeydown="return false" class="bottombutton" value="Extended Ascii" title="Toggle terminal emulation type" onclick="termToggleType()" />
837 </span>
838 <span id="terminalSizeDropDown" style="display:none">
839 <select id="termSizeList" onkeypress="return false"><option value="1">80x25</option><option value="2">100x30</option></select>
840 </span>
841 <span id="specialKeyDropDown">
842 <select id="specialkeylist" onkeypress="return false"></select>
843 <input id="specialkeylistinput" type="button" onkeypress="return false" class="bottombutton" value="Send" title="Send the selected special key" onclick="sendSpecialKey()" />
844 </span>
845 </div>
846 <div>
847 &nbsp;
848 <input type=button onkeypress="return false" onkeydown="return false" class="bottombutton" id="ctrlcbutton" value="Ctl-C" onclick="termSendKey(3,'ctrlcbutton')" />
849 <input type=button onkeypress="return false" onkeydown="return false" class="bottombutton" id="ctrlxbutton" value="Ctl-X" onclick="termSendKey(24,'ctrlxbutton')" />
850 <input type=button onkeypress="return false" onkeydown="return false" class="bottombutton" id="escbutton" value="ESC" onclick="termSendKey(27,'escbutton')" />
851 <input type=button onkeypress="return false" onkeydown="return false" class="bottombutton" id="bsbutton" value="Backspace" onclick="termSendKey(8,'bsbutton')" style="display:none" />
852 <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" />
853 </div>
854 </td>
855 </tr>
856 </table>
857 <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>
858 </div>
859 </div>
860 <div id=p13 style="display:none">
861 <div id="p13title">
862 <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>
863 <h1>Files - <span id=p13deviceName></span></h1>
864 </div>
865 <table id="p13toolbar" cellpadding="0" cellspacing="0">
866 <tr>
867 <td class="areaHead" style="line-height:22px">
868 <div class="toright2">
869 <input id="filesActionsBtn" type=button title="Perform power actions on the device" value=Actions onclick=deviceActionFunction() />
870 <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>
871 <div id="filesCustomUpperRight" style="float:left;margin-right:6px"></div>
872 <div id="filesCustomUiButtons" style="float:left"></div>
873 </div>
874 <div>
875 <input id=p13AutoConnect value="AutoConnect" onclick=autoConnectFiles(event) type="button" style="display:none" />
876 <input id=p13Connect value="Connect" cmenu="filesConnectButton" onclick=connectFiles(event,1) type="button" />
877 <input id=p13Disconnect value="Disconnect" onclick=connectFiles(event) type="button" />
878 <input id=p13Connects value="SFTP Connect" cmenu=sshPortContextMenu onclick=connectFiles(event,2) type="button" />
879 <span id=p13Status>Disconnected</span>
880 </div>
881 </td>
882 </tr>
883 <tr>
884 <td class="areaHead2" valign=bottom>
885 <div id="p13rightOfButtons" class="toright2"></div>
886 <div>
887 <input type=button style="margin-right:2px" disabled="disabled" id=p13FolderUp value="Up" onclick="p13folderup()" />
888 <input type=button style="margin-right:2px" disabled="disabled" id=p13SelectAllButton value="Select All" onclick="p13selectallfile()" />
889 <input type=button style="margin-right:2px" disabled="disabled" id=p13RenameFileButton value="Rename" onclick="p13renamefile()" />
890 <input type=button style="margin-right:2px" disabled="disabled" id=p13DeleteFileButton value="Delete" onclick="p13deletefile()" />
891 <input type=button style="margin-right:2px" disabled="disabled" id=p13ViewFileButton value="Edit" onclick="p13viewfile()" />
892 <input type=button style="margin-right:2px" disabled="disabled" id=p13NewFolderButton value="New Folder" onclick="p13createfolder()" />
893 <input type=button style="margin-right:2px" disabled="disabled" id=p13NewFileButton value="New File" onclick="p13createfile()" />
894 <input type=button style="margin-right:2px" disabled="disabled" id=p13UploadButton value="Upload" onclick="p13uploadFile()" />
895 <input type=button style="margin-right:2px" disabled="disabled" id=p13DownloadButton value="Download" onclick="p13downloadButton()" />
896 <input type=button style="margin-right:2px" disabled="disabled" id=p13CutButton value="Cut" onclick="p13copyFile(1)" />
897 <input type=button style="margin-right:2px" disabled="disabled" id=p13CopyButton value="Copy" onclick="p13copyFile(0)" />
898 <input type=button style="margin-right:2px" disabled="disabled" id=p13PasteButton value="Paste" onclick="p13pasteFile()" />
899 <input type=button style="margin-right:2px" disabled="disabled" id=p13ZipButton value="Zip" onclick="p13zipFiles()" />
900 <input type=button style="margin-right:2px" disabled="disabled" id=p13UnzipButton value="Unzip" onclick="p13unzipFile()" />
901 <input type=button style="margin-right:2px" disabled="disabled" id=p13RefreshButton value="Refresh" onclick="p13folderup(9999)" />
902 <input type=button style="margin-right:2px" disabled="disabled" id=p13FindButton value="Find" onclick="p13findfile()" />
903 <input type=button style="margin-right:2px" disabled="disabled" id=p13GoToFolderButton value="GoTo" onclick="p13gotofolder()" />
904 <input type=button style="margin-right:2px" disabled="disabled" id=p13OpenButton value="Open" onclick="p13openfilefolder()" />
905 </div>
906 </td>
907 </tr>
908 <tr>
909 <td class="areaHead3">
910 <div class="toright2">
911 <select id=p13sizedropdown onchange=p13updateFiles()>
912 <option value=0 selected="selected">Human Readable</option>
913 <option value=1 title=Bytes>Bytes</option>
914 <option value=10 title=KB>Kilobytes</option>
915 <option value=20 title=MB>Megabytes</option>
916 <option value=30 title=GB>Gigabyte</option>
917 </select>
918 <select id=p13sortdropdown onchange=p13updateFiles()>
919 <option value=1 selected="selected">Sort by name</option>
920 <option value=2>Sort by size</option>
921 <option value=3>Sort by date</option>
922 <option value=4>Descend by name</option>
923 <option value=5>Descend by size</option>
924 <option value=6>Descend by date</option>
925 </select>
926 </div>
927 <div>&nbsp;&nbsp;<span id="p13currentpath"></span></div>
928 </td>
929 </tr>
930 </table>
931 <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>
932 <div id="p13filetable" style="">
933 <div id="p13bigok" style="display:none"><b>&checkmark;</b></div>
934 <div id="p13bigfail" style="display:none"><b>&#10007;</b></div>
935 <span id="p13files"></span>
936 </div>
937 <table id="p13toolbarBottom" cellpadding=0 cellspacing=0>
938 <tr><td class=style6>&nbsp;<span id="p13bottomstatus"></span></td></tr>
939 </table>
940 </div>
941 <div id=p14 style="display:none">
942 <div id="p14title">
943 <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>
944 <div id="devListToolbarViewIcons"><div class="viewSelector" onclick=deskToggleFull(event) title="Full Screen. Hold shift to browser full screen."><div class="viewSelector5"></div></div></div>
945 <h1><span id=p14deviceNamePrefix>Intel&reg; AMT</span> - <span id=p14deviceName></span></h1>
946 </div>
947 <iframe id=p14iframe src="{{{domainurl}}}commander.ashx"></iframe>
948 </div>
949 <div id=p15 style="display:none">
950 <div id="p15title">
951 <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>
952 <h1><span id=p15deviceName></span></h1>
953 </div>
954 <table id="consoleTable" cellpadding=0 cellspacing=0>
955 <tr>
956 <td class="areaHead">
957 <div class="toright2">
958 <div id=p15coreName title="Information about current core running on this agent"></div>
959 <input type=button id=p15uploadCore value="Agent Action" onclick=p15uploadCore(event) title="Change the agent Java Script code module" />
960 <img onclick=p15downloadConsoleText() style="cursor:pointer;margin-top:6px" title="Download console text" src="images/link4.png" />
961 </div>
962 <div id="p15statetext"></div>
963 </td>
964 </tr>
965 <tr>
966 <td>
967 <div class="areaProgress"><div id="consoleprogressbar" style=""></div></div>
968 </td>
969 </tr>
970 <tr>
971 <td id=p15agentConsole>
972 <pre id=p15agentConsoleText></pre>
973 </td>
974 </tr>
975 <tr>
976 <td class="areaFoot">
977 <table style="width:100%">
978 <tr>
979 <td style="width:99%">
980 <input id=p15consoleText style=width:100% onkeyup=p15consoleSend(event) onfocus=onConsoleFocus(1) onblur=onConsoleFocus(0) />
981 </td>
982 <td>&nbsp;</td>
983 <td id="p15outputselecttd">
984 <select id=p15outputselect onchange="setupConsole()">
985 <option id="p15outputselect1" value=1>Agent</option>
986 <option id="p15outputselect3" value=3>Push</option>
987 <option id="p15outputselect2" value=2>MQTT</option>
988 </select>
989 </td>
990 <td style="width:1%"><input id="id_p15consoleClear" type="button" class="bottombutton" value="Clear" onclick="p15consoleClear()" /></td>
991 </tr>
992 </table>
993 </td>
994 </tr>
995 </table>
996 </div>
997 <div id=p16 style="display:none">
998 <div id="p16title">
999 <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>
1000 <h1>Events - <span id=p16deviceName></span></h1>
1001 </div>
1002 <table class="pTable">
1003 <tr>
1004 <td class="h1"></td>
1005 <!--<td>&nbsp;<input type=button onclick=refreshDeviceEvents() value="Refresh" /></td>-->
1006 <td class="auto-style1">
1007 Filter
1008 <select id=p16filterevents onchange=refreshDeviceEvents()>
1009 <option notransval=1 value="">All Logs</option>
1010 <option notransval=1 value=agentlog>Agent Logs</option>
1011 <option notransval=1 value=relaylog>Relay Logs</option>
1012 <option notransval=1 value=manual>Manual Logs</option>
1013 <option notransval=1 value=runcommands>Run Command Logs</option>
1014 <option notransval=1 value=batchupload>Batch Upload Logs</option>
1015 <option notransval=1 value=changenode>Change Node Logs</option>
1016 <option notransval=1 value=removenode>Remove Node Logs</option>
1017 </select>
1018 Show
1019 <select id=p16limitdropdown onchange=refreshDeviceEvents()>
1020 <option notransval=1 value=60>Last 60</option>
1021 <option notransval=1 value=120>Last 120</option>
1022 <option notransval=1 value=250>Last 250</option>
1023 <option notransval=1 value=500>Last 500</option>
1024 <option notransval=1 value=1000>Last 1000</option>
1025 </select>
1026 <a href=# onclick=p3showDownloadEventsDialog(1)><img src=images/link4.png height=10 width=10 title="Download Events" style=cursor:pointer></a>&nbsp;
1027 </td>
1028 <td class="h2"></td>
1029 </tr>
1030 </table>
1031 <div id=p16events></div>
1032 </div>
1033 <div id=p17 style="display:none;margin-left:-18px">
1034 <div id="p17title" style="margin-left:18px">
1035 <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>
1036 <div id="devListToolbarViewIcons3">
1037 <div class="viewSelector" onclick=refreshDetails(event) title="Refresh details information."><div class="viewSelector12"></div></div>
1038 <div class="viewSelector" onclick=deskToggleCpuGraph(event) title="Show device CPU and memory usage."><div class="viewSelector6"></div></div>
1039 </div>
1040 <h1>Details - <span id=p17deviceName></span></h1>
1041 </div>
1042 <div id=p17info style="overflow-y:auto">
1043 <div id=p17graph style="width:100%">
1044 <table style=width:100%>
1045 <tr>
1046 <td style=width:64px;vertical-align:top>
1047 <img src=images/details/graph64.png border=0 width=64 />
1048 </td>
1049 <td>
1050 <div class=DevSt style=margin-bottom:3px;margin-left:16px><b>Live Graph</b></div>
1051 <div style="margin-bottom:10px;margin-left:16px;height:240px;width:calc(100% - 16px);position:relative">
1052 <canvas id=deviceDetailsStats style="position:absolute;top:0;left:0;right:0;bottom:0"></canvas>
1053 </div>
1054 <div id="extraGraphValues" style="display:none;margin-left:30px;margin-bottom:15px"></div>
1055 </td>
1056 </tr>
1057 </table>
1058 </div>
1059 <div id=p17info2></div>
1060 </div>
1061 </div>
1062 <div id=p18 style="display:none">
1063 <div id="p18title">
1064 <div id="p18BackButton" style="float:left">
1065 <div class="backButton" tabindex=0 onclick=goBack() title="Back"
1066 onkeypress="if (event.key == 'Enter') goBack()">
1067 <div class="backButtonEx"></div>
1068 </div>
1069 </div>
1070 <h1>Software - <span id=p18deviceName></span></h1>
1071 </div>
1072 <div id="p18toolbar" style="padding:10px;background-color:#f5f5f5;border-bottom:1px solid #ddd">
1073 <input type="button" id="p18RefreshBtn" class="btn btn-primary btn-sm" value="Refresh" onclick="loadInstalledApps()" />
1074 <input type="text" id="p18SearchInput" class="form-control-sm" placeholder="Search software..."
1075 style="margin-left:10px;width:250px" onkeyup="filterInstalledApps()" />
1076 <label style="margin-left:15px">
1077 <input type="checkbox" id="p18ShowStore" onchange="toggleStoreApps()" /> Show Store Apps
1078 </label>
1079 <span id="p18Status" style="margin-left:20px;color:gray"></span>
1080 </div>
1081 <div id="p18info" style="overflow-y:auto;padding:10px">
1082 <div id="p18html">
1083 <div style="text-align:center;padding:50px;color:gray">
1084 <i>Click Refresh to load installed software...</i>
1085 </div>
1086 </div>
1087 </div>
1088 </div>
1089 <div id=p19 style="display:none">
1090 <div id="p19title">
1091 <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>
1092 <h1>Plugins - <span id=p19deviceName></span></h1>
1093 </div>
1094 <div id="p19headers"></div>
1095 <div id=p19pages></div>
1096 </div>
1097 <div id=p20 style="display:none">
1098 <div id=p20main style="overflow-y:auto">
1099 <div id="p20title">
1100 <picture id=MainMeshImage style=border-width:0px;height:200px;width:200px;float:right>
1101 <source type="image/webp" width=200 height=200 srcset="images/webp/mesh-256.webp" />
1102 <img alt="" width=200 height=200 src=images/mesh-256.png />
1103 </picture>
1104 <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>
1105 <h1>General - <span id=p20meshName></span></h1>
1106 </div>
1107 <p id=p20info></p>
1108 <p id=p20info2></p>
1109 </div>
1110 </div>
1111 <div id=p21 style="display:none">
1112 <div id="p21title">
1113 <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>
1114 <h1>Summary - <span id=p21meshName></span></h1>
1115 </div>
1116 <div id=p21main style="overflow-y:auto">
1117 <div style="width:100%">
1118 <div style="display:table;width:93%">
1119 <div id="meshPowerChartDiv" style="width:23%;display:inline-block;text-align:center;max-width:250px">
1120 <div style="margin:10px;font-size:16px">Power States</div>
1121 <canvas id="meshPowerChart" style="width:250px;height:250px"></canvas>
1122 </div>
1123 <div id="meshOsChartDiv" style="width:23%;display:inline-block;text-align:center;max-width:250px">
1124 <div style="margin:10px;font-size:16px">Agent Types</div>
1125 <canvas id="meshOsChart" style="width:250px;height:250px"></canvas>
1126 </div>
1127 <div id="meshConnChartDiv" style="width:23%;display:inline-block;text-align:center;max-width:250px">
1128 <div style="margin:10px;font-size:16px">Connectivity</div>
1129 <canvas id="meshConnChart" style="width:250px;height:250px"></canvas>
1130 </div>
1131 <div id="meshSecurityChartDiv" style="width:23%;display:inline-block;text-align:center;max-width:250px">
1132 <div style="margin:10px;font-size:16px">Security</div>
1133 <canvas id="meshSecurityChart" style="width:250px;height:250px"></canvas>
1134 </div>
1135 </div>
1136 </div>
1137 <p id=p21info style="overflow-y:auto"></p>
1138 </div>
1139 </div>
1140 <div id=p30 style="display:none">
1141 <div id="p30title">
1142 <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>
1143 <h1>General - <span id=p30userName></span></h1>
1144 </div>
1145 <div id="p30info" style="overflow-y:auto">
1146 <table style="width:100%" cellpadding="0" cellspacing="0">
1147 <tr>
1148 <td style=width:auto valign=top>
1149 <div id=p30html></div>
1150 </td>
1151 <td style=width:20px></td>
1152 <td style=width:200px;position:relative valign=top>
1153 <img id="p30userAuthServiceLogo" style="display:none" class=userAuthStrategyLogo width=64 height=64>
1154 <picture id=MainUserImage style="display:none;border-width:0px;height:200px;width:200px;float:right" onclick="account_manageImage(1)">
1155 <source type="image/webp" width=200 height=200 srcset="images/webp/user-256.webp" />
1156 <img alt="" width=200 height=200 src=images/user-256.png />
1157 </picture>
1158 <img id=MainUserImageEx alt="" width=200 height=200 src=images/user-256.png onclick="account_manageImage(1)" />
1159 <div style="width:100%;text-align:center"><strong><span id=MainUserState></span></strong></div>
1160 </td>
1161 </tr>
1162 </table><br />
1163 <div id=p30html2></div>
1164 <div id=p30html3></div>
1165 </div>
1166 </div>
1167 <div id=p31 style="display:none">
1168 <div id="p31title">
1169 <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>
1170 <h1>Events - <span id=p31userName></span></h1>
1171 </div>
1172 <table class="pTable">
1173 <tr>
1174 <td class="h1"></td>
1175 <!--<td>&nbsp;<input type=button onclick=refreshUsersEvents() value="Refresh" /></td>-->
1176 <td class="auto-style1">
1177 Filter
1178 <select id=p31filterevents onchange=refreshUsersEvents()>
1179 <option notransval=1 value="">All Logs</option>
1180 <option notransval=1 value=agentlog>Agent Logs</option>
1181 <option notransval=1 value=relaylog>Relay Logs</option>
1182 <option notransval=1 value=manual>Manual Logs</option>
1183 <option notransval=1 value=runcommands>Run Command Logs</option>
1184 <option notransval=1 value=batchupload>Batch Upload Logs</option>
1185 <option notransval=1 value=changenode>Change Node Logs</option>
1186 <option notransval=1 value=removenode>Remove Node Logs</option>
1187 </select>
1188 Show
1189 <select id=p31limitdropdown onchange=refreshUsersEvents()>
1190 <option notransval=1 value=60>Last 60</option>
1191 <option notransval=1 value=120>Last 120</option>
1192 <option notransval=1 value=250>Last 250</option>
1193 <option notransval=1 value=500>Last 500</option>
1194 <option notransval=1 value=1000>Last 1000</option>
1195 <option notransval=1 value="">No limit</option>
1196 </select>
1197 <a href=# onclick=p3showDownloadEventsDialog(3)><img src=images/link4.png height=10 width=10 title="Download Events" style=cursor:pointer></a>&nbsp;
1198 </td>
1199 <td class="h2"></td>
1200 </tr>
1201 </table>
1202 <div id=p31events style=""></div>
1203 </div>
1204 <div id=p40 style="display:none">
1205 <div id="p40title"><h1>My Server Stats</h1></div>
1206 <div class="areaHead">
1207 <div class="toright2">
1208 <select id=p40server style="display:none" onchange=updateServerTimelineStats()>
1209 </select>&nbsp;
1210 <select id=p40type onchange=updateServerTimelineStats()>
1211 <option value=0>Connections</option>
1212 <option value=1>Memory</option>
1213 <option value=5>CPU</option>
1214 <option value=3>Inbound Traffic</option>
1215 <option value=4>Outbound Traffic</option>
1216 </select>&nbsp;
1217 <select id=p40time onchange=updateServerTimelineHours()>
1218 <option value=3>Last 3 hours</option>
1219 <option value=8>Last 8 hours</option>
1220 <option value=24>Last day</option>
1221 <option value=168>Last week</option>
1222 <option value=720>Last 30 days</option>
1223 </select>&nbsp;
1224 <img src=images/link4.png height=10 width=10 title="Download data points (.csv)" style=cursor:pointer onclick=p40downloadEvents()>&nbsp;
1225 </div>
1226 <div>
1227 <input value="Refresh" type="button" onclick="refreshServerTimelineStats()" />
1228 &nbsp;<label><input id=p40log type="checkbox" onclick="updateServerTimelineHours()" />Log-X</label>
1229 </div>
1230 </div>
1231 <canvas id=serverMainStats style=""></canvas>
1232 </div>
1233 <div id=p41 style="display:none">
1234 <div id="p41title"><h1>My Server Tracing</h1></div>
1235 <div class="areaHead">
1236 <div class="toright2">
1237 Show
1238 <select id=p41limitdropdown onchange=displayServerTrace()>
1239 <option value=100>Last 100</option>
1240 <option value=250>Last 250</option>
1241 <option value=500>Last 500</option>
1242 <option value=1000>Last 1000</option>
1243 </select>
1244 <input value="Clear" type="button" onclick="clearServerTracing()" />
1245 <img src=images/link4.png height=10 width=10 title="Download trace (.csv)" style=cursor:pointer onclick=p41downloadServerTrace()>&nbsp;
1246 </div>
1247 <div>
1248 <input value="Tracing" type="button" onclick="setServerTracing()" />
1249 <span id="p41traceStatus">None</span>
1250 </div>
1251 </div>
1252 <div id=p41events style=""></div>
1253 </div>
1254 <div id=p42 style="display:none">
1255 <h1>My Server Plugins</h1>
1256 <div class="areaHead">
1257 <div class="toright2">
1258 </div>
1259 <div>
1260 <input value="Download Plugin" type="button" onclick="return pluginHandler.addPluginDlg();" />
1261 </div>
1262 </div>
1263 <div id="pluginRestartNotice" class="areaHead" style="background-color:gold;display:none">
1264 <div class="toright2">
1265 <input value="Refresh Agent Cores" type="button" onclick="distributeCore();return false" />
1266 </div>
1267 <div style="padding:2px">
1268 <div style="padding:2px"><b>Notice:</b> Plugins have been altered, this may require agent core update.</div>
1269 </div>
1270 </div>
1271 <table id="p42tbl">
1272 <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>
1273 </table>
1274 <div id="pluginNoneNotice" style="width:100%;text-align:center;padding-top:10px;display:none"><i>No plugins on server.</i></div>
1275 </div>
1276 <div id=p43 style="display:none">
1277 <div id="p43BackButton"><div class="backButton" tabindex=0 onmouseup=go(42) title="Back" onkeypress="if (event.key == 'Enter') go(42)"><div class="backButtonEx"></div></div></div>
1278 <h1>My Server Plugins - <span id="p43title"></span></h1>
1279 <iframe id="p43iframe" frameBorder=0 style="width:100%;height:calc(100vh - 245px);max-height:calc(100vh - 245px)"></iframe>
1280 </div>
1281 <div id=p50 style="display:none">
1282 <div id="p50title"><h1>My User Groups</h1></div>
1283 <table class="pTable">
1284 <tr>
1285 <td class="h1"></td>
1286 <td class="style14">
1287 <div style="float:right">
1288 </div>
1289 <div id="p50userGroupOps">
1290 <input type=button id=UsersGroupsSelectAllButton onclick="p50usersSelectallButtonFunction()" value="Select All" />
1291 <input type=button id=UsersGroupsGroupActionButton disabled="disabled" value="Group Action" onclick=p50usersGroupActionFunction() />
1292 <input id=NewUserGroupButton type=button onclick=showCreateUserGroupDialog(1) value="New Group..." />
1293 <input id=DuplicateUserGroupButton type=button style=display:none onclick=showCreateUserGroupDialog(2) value="Duplicate Group..." />
1294 </div>
1295 </td>
1296 <td class="h2"></td>
1297 </tr>
1298 </table>
1299 <div id="p50groups"></div>
1300 </div>
1301 <div id=p51 style="display:none">
1302 <div id="p51title">
1303 <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>
1304 <h1>User Group - <span id=p51groupName></span></h1>
1305 </div>
1306 <div id="p51info" style="overflow-y:auto">
1307 <table style="width:100%" cellpadding="0" cellspacing="0">
1308 <tr>
1309 <td style=width:auto valign=top>
1310 <div id=p51group></div>
1311 </td>
1312 <td style=width:20px></td>
1313 <td style=width:200px valign=top>
1314 <picture id=MainUserImage style=border-width:0px;height:200px;width:200px;float:right>
1315 <source type="image/webp" width=200 height=200 srcset="images/webp/group-256.webp" />
1316 <img alt="" width=200 height=200 src=images/group-256.png />
1317 </picture>
1318 <div style="width:100%;text-align:center"><strong><span id=MainUserState></span></strong></div>
1319 </td>
1320 </tr>
1321 </table>
1322 <div id=p51group2></div>
1323 <br />
1324 </div>
1325 </div>
1326 <div id=p52 style="display:none">
1327 <div id="p52title">
1328 <h1>My User Recordings</h1>
1329 </div>
1330 <table class="pTable">
1331 <tr>
1332 <td class="h1"></td>
1333 <td class="style14">
1334 <div style="float:right">
1335 <input type=button onclick=openRecodringPlayer() value="Open Player..." />
1336 </div>
1337 <div>
1338 <input type=button onclick=refreshRecodings() value="Refresh" />
1339 </div>
1340 </td>
1341 <td class="h2"></td>
1342 </tr>
1343 </table>
1344 <div id=p52recordings style="overflow-y:auto"></div>
1345 </div>
1346 <div id=p60 style="display:none">
1347 <div id="p60title">
1348 <h1>My Reports</h1>
1349 </div>
1350 <table class="pTable">
1351 <tr>
1352 <td class="h1"></td>
1353 <td class="style14">
1354 <div style="float:right;line-height:22px">
1355 <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;
1356 </div>
1357 <div>
1358 <input type=button onclick=generateReportDialog() value="Generate Report..." />
1359 </div>
1360 </td>
1361 <td class="h2"></td>
1362 </tr>
1363 </table>
1364 <div id=p60report style="overflow-y:auto"></div>
1365 </div>
1366 <br id="column_l_bottomgap" />
1367 </div>
1368 <div id="footer">
1369 <div class="footer1">{{{footer}}}</div>
1370 <div class="footer2">
1371 <a id="verifyEmailId2" style="display:none" href=# onclick="account_showVerifyEmail()">Verify Email</a>
1372 &nbsp;<a id="termsLinkFooter" href=terms>Terms &amp; Privacy</a>
1373 </div>
1374 </div>
1375 <div id=dialog class="noselect" style="display:none">
1376 <div id=dialogHeader>
1377 <div tabindex=0 id=id_dialogclose onclick=setDialogMode() onkeypress="if (event.key == 'Enter') setDialogMode()">&#x2716;</div>
1378 <div id=id_dialogtitle></div>
1379 </div>
1380 <div id=dialogBody>
1381 <div id=dialog1>
1382 <div id=id_dialogMessage style=""></div>
1383 </div>
1384 <div id=dialog2 style="">
1385 <div id=id_dialogOptions></div>
1386 </div>
1387 <div id=dialog3 style="">
1388 <div id=d3upload>
1389 <div>File Selection</div>
1390 <select id=d3uploadMode onchange=d3modechange()>
1391 <option value=1>Local file upload</option>
1392 <option value=2>Server file selection</option>
1393 </select>
1394 </div>
1395 <div id=d3localmode style="display:none">
1396 <div>Upload File</div>
1397 <form id=d3localmodeform method=post enctype=multipart/form-data action=uploadfile.ashx target=fileUploadFrame>
1398 <input type=text id=d3auth name=auth style="display:none" />
1399 <input type=text id=d3filter name=filter style="display:none" />
1400 <input type=text id=d3attrib name=attrib style="display:none" />
1401 <input type=file id=d3localFile name=files onchange=d3setActions() />
1402 <input type=submit id=d3submit style="display:none" />
1403 </form>
1404 </div>
1405 <div id=d3servermode>
1406 <div id=d3serveraction valign=bottom>
1407 <input type=button id=p3FolderUp disabled="disabled" onclick=d3folderup() value="Up" />&nbsp;<span id=p3CurrentFolder></span>
1408 </div>
1409 <div id=d3serverfiles></div>
1410 </div>
1411 </div>
1412 <div id=dialog4 style="">
1413 <input id="d4WrapButton" type="button" value="Wrap On" onclick="d4ToggleWrap()" />
1414 <input id="d4SizeButton" type="button" value="Small" onclick="d4ToggleSize()" />
1415 <input id="d4EncodingButton" type="button" value="Raw" onclick="d4ToggleEncoding()" />
1416 <input id="d4LineBreakButton" type="button" value="Windows" onclick="d4ToggleLineBreak()" />
1417 <textarea id=d4editorarea autocomplete="off" style="height:calc(100vh - 286px);width:100%;overflow:scroll;resize:none;white-space:pre"></textarea>
1418 </div>
1419 <div id=dialog7 style="">
1420 <div class="dtab">
1421 <button id="td7meshkvm" class="tablinks" onclick="changeDesktopSettingsTab(event, 'd7meshkvm')">Agent</button>
1422 <button id="td7rdpkvm" class="tablinks" onclick="changeDesktopSettingsTab(event, 'd7rdpkvm')">RDP</button>
1423 <button id="td7amtkvm" class="tablinks" onclick="changeDesktopSettingsTab(event, 'd7amtkvm')">Intel&reg; AMT</button>
1424 </div>
1425 <div id="d7meshkvm" class="tabcontent">
1426 <!--<h4>Agent Remote Desktop</h4>-->
1427 <div style="margin-top:8px">
1428 <div>Quality</div>
1429 <select id="d7bitmapquality" dir="rtl"></select>
1430 </div>
1431 <div>
1432 <div>Scaling</div>
1433 <select id="d7bitmapscaling" style="" dir="rtl">
1434 <option selected=selected value=1024>100%</option>
1435 <option value=896>87.5%</option>
1436 <option value=768>75%</option>
1437 <option value=640>62.5%</option>
1438 <option value=512>50%</option>
1439 <option value=384>37.5%</option>
1440 <option value=256>25%</option>
1441 <option value=128>12.5%</option>
1442 </select>
1443 </div>
1444 <div>
1445 <div>Frame rate</div>
1446 <select id="d7framelimiter" dir="rtl">
1447 <option selected=selected value=50>Fast</option>
1448 <option value=100>Medium</option>
1449 <option value=400>Slow</option>
1450 <option value=1000>Very slow</option>
1451 </select>
1452 </div>
1453 <div>
1454 <div>Encoding</div>
1455 <select id="d7encoding" dir="rtl">
1456 <option value=1>JPEG</option>
1457 <option value=2>PNG</option>
1458 <option value=3>TIFF</option>
1459 <option selected=selected value=4>WEBP</option>
1460 </select>
1461 </div>
1462 <div id="d7desktopOtherSettings">
1463 <div>Other Settings</div>
1464 <div id="d7otherset2" style="display:block">
1465 <label style="display:block"><input type="checkbox" id="d7deskSwapMouse" />Swap Mouse Buttons</label>
1466 <label style="display:block"><input type="checkbox" id="d7deskrmw" />Reverse Mouse Wheel</label>
1467 <label style="display:block"><input type="checkbox" id="d7deskRemoteKeyMap" />Use Remote Keyboard Map</label>
1468 <label style="display:block" id="d7deskAutoClipboardLabel"><input type="checkbox" id="d7deskAutoClipboard" />Automatic Clipboard</label>
1469 <label style="display:block" id="d7deskAutoLockLabel"><input type="checkbox" id="d7deskAutoLock" />Lock on Disconnect</label>
1470 </div>
1471 </div>
1472 </div>
1473 <div id="d7amtkvm" class="tabcontent">
1474 <!--<h4>Intel&reg; AMT Hardware KVM</h4>-->
1475 <div style="margin-top:8px">
1476 <div>Image Encoding</div>
1477 <select id="d7desktopmode">
1478 <option value="1">RLE8, Fastest</option>
1479 <option value="2">RLE16, Recommended</option>
1480 <option value="3">RAW8, Slow</option>
1481 <option value="4">RAW16, Very Slow</option>
1482 </select>
1483 </div>
1484 <div>
1485 <div>Other Settings</div>
1486 <div id="d7otherset" style="display:block">
1487 <label style="display:block"><input type="checkbox" id="d7showfocus" />Show Focus Tool</label>
1488 <label style="display:block"><input type="checkbox" id="d7showcursor" />Show Local Mouse Cursor</label>
1489 <label style="display:block"><input type="checkbox" id="d7localKeyMap" />Local Keyboard Map</label>
1490 <label style="display:block"><input type="checkbox" id="d7kvmrmw" />Reverse Mouse Wheel</label>
1491 </div>
1492 </div>
1493 </div>
1494 <div id="d7rdpkvm" class="tabcontent">
1495 <!--<h4>Remote Desktop Protocol</h4>-->
1496 <div style="margin-top:8px">
1497 <div>Display Size</div>
1498 <select id="d7rdpsize">
1499 <option notransval=1 value="canvas">Canvas Size</option>
1500 <option notransval=1 value="browser">Browser Size</option>
1501 <option notransval=1 value="screen">Screen Size</option>
1502 <option notransval=1 value="640x480">640x480</option>
1503 <option notransval=1 value="1024x768">1024x768</option>
1504 <option notransval=1 value="1280x800">1280x800</option>
1505 <option notransval=1 value="1440x900">1440x900</option>
1506 <option notransval=1 value="1600x900">1600x900</option>
1507 <option notransval=1 value="1680x1050">1680x1050</option>
1508 <option notransval=1 value="1920x1080">1920x1080</option>
1509 </select>
1510 </div>
1511 <div>
1512 <div>Options</div>
1513 <div id="d7rdpflags" style="display:block">
1514 <label style="display:block"><input type="checkbox" id="d7rdp1" />Disable Wallpaper</label>
1515 <label style="display:block"><input type="checkbox" id="d7rdp2" />Disable Full Window Drag</label>
1516 <label style="display:block"><input type="checkbox" id="d7rdp3" />Disable Menu Animations</label>
1517 <label style="display:block"><input type="checkbox" id="d7rdp4" />Disable Theming</label>
1518 <label style="display:block"><input type="checkbox" id="d7rdp6" />Disable Cursor Shadow</label>
1519 <label style="display:block"><input type="checkbox" id="d7rdp7" />Disable Cursor Settings</label>
1520 <label style="display:block"><input type="checkbox" id="d7rdp8" />Enable Font Smoothing</label>
1521 <label style="display:block"><input type="checkbox" id="d7rdp9" />Enable Desktop Composision</label>
1522 <label style="display:block"><input type="checkbox" id="d7rdpclip" />Automatic Clipboard</label>
1523 <label style="display:block"><input type="checkbox" id="d7rdpsmb" />Swap Mouse Buttons</label>
1524 <label style="display:block"><input type="checkbox" id="d7rdprmw" />Reverse Mouse Wheel</label>
1525 </div>
1526 </div>
1527 </div>
1528 </div>
1529 </div>
1530 <div id="idx_dlgButtonBar">
1531 <input id="idx_dlgCancelButton" type="button" value="Cancel" style="" onclick="dialogclose(0)" />
1532 <input id="idx_dlgOkButton" type="button" value="OK" style="" onclick="dialogclose(1)" />
1533 <div><input id="idx_dlgDeleteButton" type="button" value="Delete" style="display:none" onclick="dialogclose(2)" /></div>
1534 <div id="idx_dlgMoreButtons">
1535 <a href=# id=idx_dlgMoreButtons1 style=cursor:pointer;color:gray;text-decoration:none title="Toggle advanced options" onclick=MoreToggle(true)>&#x25BC;</a>
1536 <a href=# id=idx_dlgMoreButtons2 style=cursor:pointer;color:gray;text-decoration:none title="Toggle advanced options" onclick=MoreToggle(false)>&#x25B2;</a>
1537 </div>
1538 </div>
1539 </div>
1540 <iframe name="fileUploadFrame" style="display:none"></iframe>
1541 <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>
1542 <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>
1543 <audio id="chimes"><source src="sounds/chimes.mp3" type="audio/mp3" /></audio>
1544 <iframe style=display:none name="fileDownloadFrame"></iframe>
1545 </div>
1546 <script type="text/javascript">
1547 'use strict';
1548 var random = '{{{randomlength}}}' // Random length string for BREACH mitigation
1549
1550 // Process server-side web state
1551 var webState = '{{{webstate}}}';
1552 if (webState != '') { webState = JSON.parse(decodeURIComponent(webState)); }
1553 if ((webState == null) || (typeof webState != 'object')) { webState = {}; }
1554 for (var i in webState) { try { localStorage.setItem(i, webState[i]); } catch (ex) {} }
1555 if (webState && !webState.loctag) { try { delete localStorage.removeItem('loctag'); } catch (ex) { } }
1556
1557 var args, urlargs;
1558 var autoReconnect = true;
1559 var powerStatetable = ['', "Powered", "Sleep", "Sleep", "Sleep", "Hibernating", "Power off", "Present"];
1560 var StatusStrs = ["Disconnected", "Connecting...", "Setup...", "Connected", "Intel&reg; AMT Connected"];
1561 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", "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", "ARMVIRT32 (OpenWRT)", "RISC-V x86-64"];
1562 var agentsStrNoAgent = ['', '', '', '', "Windows", '', "Linux", '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', "Apple"];
1563 var domainStates = ["None", "Azure AD", "On-Premises AD", "Hybrid AD", "Microsoft Account"];
1564 var sort = 0;
1565 var searchFocus = 0;
1566 var mapSearchFocus = 0;
1567 var userSearchFocus = 0;
1568 var consoleFocus = 0;
1569 var showRealNames = false;
1570 var meshserver = null;
1571 var meshes = {};
1572 var loginTokens = {};
1573 var meshcount = 0;
1574 var nodes = null;
1575 var usergroups = null;
1576 var filetree = {};
1577 var userinfo = null;
1578 var serverinfo = null;
1579 var events = [];
1580 var users = null;
1581 var wssessions = null;
1582 var stars = {}; // Devices that have been "stared" by the user.
1583 var nodeShortIdent = 0;
1584 var desktop;
1585 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, agentencoding: 4 };
1586 var multidesktopsettings = { quality: 20, scaling: 128, framerate: 1000, agentencoding: 4 };
1587 var terminal;
1588 var files;
1589 var debugLevel = parseInt('{{{debuglevel}}}');
1590 var features = parseInt('{{{features}}}');
1591 var features2 = parseInt('{{{features2}}}');
1592 var features3 = parseInt('{{{features3}}}');
1593 var sessionTime = parseInt('{{{sessiontime}}}');
1594 var webRelayPort = parseInt('{{{webRelayPort}}}');
1595 var webRelayDns = '{{{webRelayDns}}}';
1596 var hidePowerTimeline = '{{{hidePowerTimeline}}}';
1597 var showNotesPanel = '{{{showNotesPanel}}}';
1598 var sessionRefreshTimer = null;
1599 var domain = '{{{domain}}}';
1600 var domainUrl = '{{{domainurl}}}';
1601 var authCookie = '{{{authCookie}}}';
1602 var authRelayCookie = '{{{authRelayCookie}}}';
1603 var logoutControls = JSON.parse(decodeURIComponent('{{{logoutControls}}}'));
1604 var authCookieRenewTimer = null;
1605 var multiDesktop = {};
1606 var serverPublicNamePort = '{{{serverDnsName}}}:{{{serverPublicPort}}}';
1607 var amtScanResults = null;
1608 var debugmode = 0;
1609 var windowsBrowser = detectWindowsBrowser();
1610 var attemptWebRTC = ((features & 128) != 0);
1611 var webrtcconfiguration = '{{{webrtcconfig}}}';
1612 if (webrtcconfiguration == '') { webrtcconfiguration = null; } else { try { webrtcconfiguration = JSON.parse(decodeURIComponent(webrtcconfiguration)); } catch (ex) { console.log('Invalid WebRTC config: "' + webrtcconfiguration + '".'); webrtcconfiguration = null; } }
1613 var passRequirements = '{{{passRequirements}}}';
1614 if (passRequirements != '') { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); }
1615 var customui = '{{{customui}}}';
1616 if (customui != '') { customui = JSON.parse(decodeURIComponent(customui)); } else { customui = null; }
1617 var deskAspectRatio = 0;
1618 try { deskAspectRatio = parseInt(getstore('deskAspectRatio', '0')); } catch (ex) { }
1619 var uiMode = parseInt(getstore('uiMode', 1));
1620 var webPageStackMenu = false;
1621 var webPageFullScreen = true;
1622 var nightMode = setNightMode();
1623 var footerBar = (getstore('footerBar', '1') == '1');
1624 var sessionActivity = Date.now();
1625 var updateSessionTimer = null;
1626 var pluginHandlerBuilder = {{{pluginHandler}}};
1627 var pluginHandler = null;
1628 if (pluginHandlerBuilder != null) { pluginHandler = new pluginHandlerBuilder(); }
1629 var installedPluginList = null;
1630 var goBackStack = [];
1631 var CollapsedGroups = {};
1632 var collapseGroups = '{{{collapseGroups}}}';
1633 try { CollapsedGroups = JSON.parse(getstore('_collapse', '{}')); } catch(ex) {}
1634 var deviceViewSettings = {};
1635 try { deviceViewSettings = JSON.parse(getstore('_deviceViewSettings', '{}')); } catch(ex) {}
1636 var userViewSettings = {};
1637 try { userViewSettings = JSON.parse(getstore('_usersViewSettings', '{}')); } catch(ex) {}
1638 var xterm = null;
1639 var xtermfit = null;
1640 var xtermimage = null;
1641 var xtermResizeTimer = null;
1642 var miscState = {};
1643 var checkedNodeids = {};
1644 var deskKeyboardShortcuts = [];
1645 var deskKeyboardStrings = [];
1646 var deskLastClipboardSent = null;
1647 var deskLastClipboardReceived = null;
1648 var requestedLastConnects = false;
1649 var devicePagingState = null;
1650
1651 // Console Message Display Timers
1652 var p11DeskConsoleMsgTimer = null;
1653 var p12TermConsoleMsgTimer = null;
1654 var p13FilesConsoleMsgTimer = null;
1655
1656 //
1657 // INSTALLED SOFTWARE
1658 //
1659 var installedAppsData = null; // Gespeicherte App-Daten
1660 var installedAppsLoading = false; // Ladestatus
1661 var showStoreApps = false; // Store Apps anzeigen?
1662
1663 /*
1664 // Check browser dark mode preference
1665 var prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
1666 var prefersLightScheme = window.matchMedia('(prefers-color-scheme: light)').matches;
1667 console.log(prefersDarkScheme, prefersLightScheme);
1668 var mql = window.matchMedia('(prefers-color-scheme: dark)');
1669 mql.addEventListener('change', function() { console.log('Dark Change'); });
1670 */
1671
1672 // Check if WebP is supported
1673 var webpSupport = false;
1674 check_webp_feature('lossy', function (f, x) {
1675 webpSupport = x;
1676 if (!x) {
1677 d7encoding.options[1].disabled = true;
1678 d7encoding.value = 1;
1679 }
1680 });
1681
1682 function startup() {
1683 if ((features & 32) == 0) {
1684 // Guard against other site's top frames (web bugs).
1685 var loc = null;
1686 try { loc = top.location.toString().toLowerCase(); } catch (e) { }
1687 if (top != self && (loc == null || top.active == false)) { top.location = self.location; return; }
1688 }
1689
1690 // Fetch URL arguments & do sanitation
1691 urlargs = parseUriArgs();
1692 if (urlargs.key != null) { urlargs.key = "" + urlargs.key; }
1693 if (urlargs.key && (isAlphaNumeric(urlargs.key) == false)) { delete urlargs.key; }
1694 if (urlargs.locale && (isAlphaNumeric(urlargs.locale) == false)) { delete urlargs.locale; }
1695 delete urlargs.user;
1696 delete urlargs.pass;
1697 delete urlargs.viewmode;
1698 delete urlargs.gotonode;
1699 delete urlargs.gotodevicename;
1700 delete urlargs.gotodevicername;
1701 delete urlargs.gotodeviceip;
1702 delete urlargs.gotomesh;
1703 delete urlargs.gotouser;
1704 delete urlargs.gotougrp;
1705
1706 // Fix links if a loginKey is used
1707 if (urlargs.key) { Q('termsLinkFooter').href += '?key=' + urlargs.key; }
1708
1709 // Check if we are in debug mode
1710 args = parseUriArgs();
1711 if (args.key && (isAlphaNumeric(args.key) == false)) { delete args.key; }
1712 if (args.locale && (isAlphaNumeric(args.locale) == false)) { delete args.locale; }
1713
1714 if (!args.locale) { var x = getstore('loctag', 0); if ((x != null) && (x != '*')) { args.locale = x; } }
1715 debugmode = args.debug;
1716
1717 //attemptWebRTC = false; // For now, default WebRTC off unless we set it in the URL.
1718 if (args.webrtc != null) { attemptWebRTC = (args.webrtc == 1); }
1719
1720 QV('p13AutoConnect', debugmode); // Files
1721 QV('autoconnectbutton2', debugmode); // Terminal
1722 QV('autoconnectbutton1', debugmode); // Desktop
1723 //QV('DeskClip', debugmode); // Clipboard feature, not completed so show in in debug mode only.
1724
1725 if (nightMode) { QC('body').add('night'); QS('body')['background-color'] = '#000'; }
1726 toggleFullScreen();
1727
1728 // Setup stared devices
1729 try { stars = JSON.parse(getstore('stars', '{}')); } catch (ex) {}
1730
1731 // Setup page visuals
1732 var hide = 0;
1733 var globalHide = parseInt('{{{hide}}}');
1734 if (globalHide || args.hide) {
1735 if (args.hide) { hide = parseInt(args.hide); }
1736 if (globalHide) { hide = (hide | globalHide); }
1737 }
1738 args.hide = hide;
1739 QV('uiViewButton5', !(args.hide & 4)); // Hide the footer toggle button if footer is hidden anyway.
1740 adjustPanels();
1741
1742 // Setup logout control
1743 var logoutControl = '';
1744 if (logoutControls) {
1745 if (logoutControls.name != null) { logoutControl = ('<span onmouseup=go(2) CLASS="LogoffLinkColor" style="cursor:pointer">' + format("Welcome {0}.", logoutControls.name) + '</span>'); }
1746 if (logoutControls.logoutUrl != null) { logoutControl += format(' <a href="' + logoutControls.logoutUrl + '" CLASS="LogoffLinkColor">' + "Logout" + '</a>'); }
1747 }
1748 if (args.hide & 1) { QH('logoutControlSpan2', logoutControl); } else { QH('logoutControlSpan', logoutControl); }
1749
1750 // Setup the context menu
1751 document.onclick = function (e) { hideContextMenu(); }
1752 document.onkeypress = ondockeypress;
1753 document.onkeydown = ondockeydown;
1754 document.onkeyup = ondockeyup;
1755 //window.addEventListener('focus', ondocfocus, false);
1756 window.addEventListener('blur', ondocblur, false);
1757 window.onresize = function () {
1758 hideContextMenu();
1759 mainUpdate(512);
1760 browserfullscreen = isBrowserFullscreen();
1761 QV('DeskESC', browserfullscreen);
1762 if ((xtermfit != null) && (xxcurrentView == 12)) { xtermfit.fit(); }
1763 }
1764 setTimeout(function() {
1765 if (urlargs.filter) { Q('SearchInput').value = urlargs.filter; Q('KvmSearchInput').value = urlargs.filter; }
1766 mainUpdate(512);
1767 }, 200);
1768
1769 // open notes markdown links in new tab
1770 if (DOMPurify) {
1771 DOMPurify.addHook('afterSanitizeAttributes', function (node) {
1772 if ('target' in node) {
1773 node.setAttribute('target', '_blank');
1774 node.setAttribute('rel', 'noopener noreferrer');
1775 }
1776 if (!node.hasAttribute('target') && (node.hasAttribute('xlink:href') || node.hasAttribute('href'))) {
1777 node.setAttribute('xlink:show', 'new');
1778 }
1779 });
1780 }
1781
1782 // Show the modern ui switcher
1783 QV('textnewui', ((features2 & 0x40000000) == 0) ? false : true);
1784
1785 // Show Connect All and Disconnect All
1786 QV('KvmDesktopButtons', ((features3 & 0x00000001) == 0) ? true : false);
1787
1788 // Connect to the mesh server
1789 meshserver = MeshServerCreateControl(domainUrl);
1790 meshserver.onStateChanged = onStateChanged;
1791 meshserver.onMessage = onMessage;
1792 meshserver.trace = args.trace;
1793 meshserver.Start();
1794
1795 // Setup page controls
1796 Q('sortselect').selectedIndex = sort = getstore('sort', 0);
1797 Q('sizeselect').selectedIndex = getstore('viewsize', 1);
1798 Q('KvmSearchInput').value = Q('SearchInput').value = getstore('_search', '');
1799 showRealNames = (getstore('showRealNames', 0) == 1);
1800 Q('RealNameCheckBox').checked = showRealNames;
1801 Q('DevFilterSelect').value = getstore('devFilterSelect', 0);
1802 Q('viewselect').value = getstore('deviceView', 1);
1803 Q('DeskControl').checked = (getstore('DeskControl', 1) == 1);
1804 QV('accountChangeEmailAddressSpan', (features & 0x200000) == 0);
1805
1806 // Display the page devices
1807 mainUpdate(3)
1808 for (var j = 1; j < 5; j++) { Q('devViewButton' + j).classList.remove('viewSelectorSel'); }
1809 Q('devViewButton' + Q('viewselect').value).classList.add('viewSelectorSel');
1810
1811 // Setup upload drag & drop for server files
1812 Q('p5filetable').addEventListener('drop', p5fileDragDrop, false);
1813 Q('p5filetable').addEventListener('dragover', p5fileDragOver, false);
1814 Q('p5filetable').addEventListener('dragleave', p5fileDragLeave, false);
1815 //Q('p5fileCatchAllInput').addEventListener('drop', p5fileDragDrop, false);
1816 //Q('p5fileCatchAllInput').addEventListener('dragover', p5fileDragOver, false);
1817 //Q('p5fileCatchAllInput').addEventListener('dragleave', p5fileDragLeave, false);
1818
1819 // Setup drag & drop for device desktop
1820 Q('deskarea0').addEventListener('drop', p11fileDragDrop, false);
1821 Q('deskarea0').addEventListener('dragover', p11fileDragOver, false);
1822 Q('deskarea0').addEventListener('dragleave', p11fileDragLeave, false);
1823
1824 // Setup upload drag & drop for device files
1825 Q('p13filetable').addEventListener('drop', p13fileDragDrop, false);
1826 Q('p13filetable').addEventListener('dragover', p13fileDragOver, false);
1827 Q('p13filetable').addEventListener('dragleave', p13fileDragLeave, false);
1828
1829 // Timeline update interval
1830 setInterval(updateDeviceTimeline, 120000); // Check every 2 minutes
1831
1832 // Load desktop settings
1833 var t = null;
1834 try { t = localStorage.getItem('desktopsettings'); } catch (ex) {}
1835 if (t != null) { desktopsettings = JSON.parse(t); }
1836 t = null;
1837 try { t = localStorage.getItem('multidesktopsettings'); } catch (ex) {}
1838 if (t != null) { multidesktopsettings = JSON.parse(t); }
1839 applyDesktopSettings();
1840
1841 // Terminal special keys
1842 var x = '';
1843 for (var c = 1; c < 27; c++) x += '<option value=\'' + c + '\'>' + "Ctrl" + '-' + String.fromCharCode(64 + c) + ' (' + c + ')</option>';
1844 QH('specialkeylist', x);
1845
1846 // Setup server stats panels
1847 setupGeneralServerStats();
1848 setupServerTimelineStats();
1849
1850 // Setup the user interface in the right mode
1851 userInterfaceSelectMenu();
1852
1853 // Setup Mesh summary panel
1854 setupMeshSummaryStats();
1855
1856 // If SSPI or LDAP authentication not used, allow batch account creation.
1857 QV('p4UserBatchCreate', (features & 0x00080000) == 0);
1858
1859 // Set the file editor
1860 d4EditWrapVal = Number(getstore('editorWrap', 0));
1861 d4EditSizeVal = Number(getstore('editorSize', 0));
1862 d4EditEncodingVal = Number(getstore('editorEncoding', 0));
1863 d4EditLineBreakVal = Number(getstore('editorLineBreak', 0));
1864 d4ToggleWrap(true);
1865 d4ToggleSize(true);
1866 d4ToggleEncoding(true);
1867 d4ToggleLineBreak(true);
1868 if (pluginHandler != null) pluginHandler.callHook('onWebUIStartupEnd');
1869
1870 // Deleted non-english style and fix all topbar titles
1871 if ((['en','ko','ja','zh-chs','zh-cht'].indexOf('{{{lang}}}') == -1) && ('{{{lang}}}'.toLowerCase() != '')) { QC('body').add('nonenglish'); }
1872 var elements = document.getElementsByClassName('topbar_td');
1873 for (var i in elements) { if (elements[i].innerHTML) { elements[i].innerHTML = elements[i].innerHTML.split(' ').join('&nbsp;'); } }
1874
1875 // Custom UI
1876 if (customui != null) {
1877 if (customui.devicesbarbuttons) {
1878 var x = '';
1879 for (var i in customui.devicesbarbuttons) {
1880 var disabled = ((customui.devicesbarbuttons[i].selection == 'one') || (customui.devicesbarbuttons[i].selection == 'many'))?'disabled':'';
1881 x += '<input id="cui:' + i + '" type="button" value="' + customui.devicesbarbuttons[i].name + '" ' + disabled + ' onclick=customUIAction(event,"devicesbarbuttons") />';
1882 }
1883 QH('devsCustomUIBar', x);
1884 }
1885 if (customui.desktopbuttons) {
1886 var x = '';
1887 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") />'; }
1888 QH('desktopCustomUiButtons', x);
1889 }
1890 if (customui.terminalbuttons) {
1891 var x = '';
1892 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") />'; }
1893 QH('terminalCustomUiButtons', x);
1894 }
1895 if (customui.filesbuttons) {
1896 var x = '';
1897 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") />'; }
1898 QH('filesCustomUiButtons', x);
1899 }
1900 }
1901
1902 // Session Refresh Timer
1903 if (sessionTime >= 10) { sessionRefreshTimer = setTimeout(refreshCookieSession, Math.round((sessionTime * 60000) * 0.8)); }
1904
1905 // Set the user's desktop shortcut keys
1906 deskKeyboardShortcuts = [];
1907 var deskKeyboardShortcutsStr = getstore('deskKeyShortcuts', '0x0A002E,0x100000,0x100028,0x100026,0x10004C,0x10004D,0x11004D,0x100052,0x020073,0x080057,0x020009,0x100025,0x100027').split(',');
1908 for (var i in deskKeyboardShortcutsStr) { if (deskKeyboardShortcutsStr[i] != "") { deskKeyboardShortcuts.push(parseInt(deskKeyboardShortcutsStr[i])); } }
1909 updateDeskShortcutKeys();
1910
1911 // Set the user's desktop strings
1912 try { deskKeyboardStrings = JSON.parse(getstore('deskStrings', '[]')); } catch (ex) {}
1913 updateDesktopStrings();
1914
1915 // Override the collapse button text
1916 updateCollapseAllButton();
1917
1918 // Make the dialog box movable
1919 dialogBoxDrag();
1920
1921 // Fix links
1922 if (urlargs.key) { Q('p6backuplink').href += '?key=' + urlargs.key; }
1923
1924 // Hide night mode button if needed
1925 QV('uiViewButton4', (features2 & 0x00300000) == 0);
1926
1927 // Fix HTML words that in english have two or more meanings
1928 Q('DeskType').value = multiTranslate("[KeyboardTyping]|Type");
1929
1930 // Show the "Scroll to top" button
1931 QV('ScrollToTopButton', (features2 & 0x08000000) != 0);
1932 }
1933
1934 function refreshCookieSession() {
1935 var xdr = null;
1936 try { xdr = new XDomainRequest(); } catch (e) { }
1937 if (!xdr) xdr = new XMLHttpRequest();
1938 xdr.open('GET', window.location.origin + domainUrl + 'refresh.ashx');
1939 xdr.timeout = 15000;
1940 xdr.onload = function () { sessionRefreshTimer = setTimeout(refreshCookieSession, Math.round((sessionTime * 60000) * 0.8)); };
1941 xdr.onerror = xdr.ontimeout = function () { sessionRefreshTimer = null; };
1942 xdr.send();
1943 }
1944
1945 // Generic handling of custom actions
1946 function customUIAction(e, section) {
1947 var id = e.srcElement.id;
1948 if (id.startsWith('cui:') == false) return;
1949 var info = customui[section][id.substring(4)];
1950 if (info == null) return;
1951 if (section == 'devicesbarbuttons') {
1952 var elements = document.getElementsByClassName('DeviceCheckbox'), selectedDevices = [];
1953 for (var i = 0; i < elements.length; i++) { if (elements[i].checked === true) { selectedDevices.push(elements[i].defaultValue.substring(6)); } }
1954 if (typeof info.action == 'string') {
1955 if (info.action == 'event') { meshserver.send({ action: 'uicustomevent', section: section, element: id.substring(4), selectedDevices: selectedDevices, logmsg: info.logmsg }); }
1956 if (info.action.startsWith('dialog:')) { showCustomUiDialog(info.action.substring(7), { section: section, element: id.substring(4), selectedDevices: selectedDevices }); }
1957 if (info.deselectdevices) { for (var i = 0; i < elements.length; i++) { elements[i].checked = false; } checkedNodeids = {}; p1updateInfo(); }
1958 }
1959 }
1960 if ((section == 'devicebuttons') || (section == 'desktopbuttons') || (section == 'terminalbuttons') || (section == 'filesbuttons')) {
1961 var selectedDevices = [ currentNode._id ];
1962 if (typeof info.action == 'string') {
1963 if (info.action == 'event') { meshserver.send({ action: 'uicustomevent', section: section, element: id.substring(4), selectedDevices: selectedDevices, logmsg: info.logmsg }); }
1964 if (info.action.startsWith('dialog:')) { showCustomUiDialog(info.action.substring(7), { section: section, element: id.substring(4), selectedDevices: selectedDevices }); }
1965 }
1966 }
1967 }
1968
1969 // Display a generic custom UI dialog
1970 function showCustomUiDialog(name, tag) {
1971 if ((customui == null) || (customui.dialogs == null) || (typeof customui.dialogs[name] != 'object')) return;
1972 var x = '', dialog = customui.dialogs[name], buttons = 3;
1973 if (typeof dialog.text == 'string') { x += '<div style=margin-bottom:8px>' + dialog.text + '</div>'; }
1974 if (typeof dialog.buttons == 'number') { buttons = dialog.buttons; }
1975 if (typeof dialog.elements == 'object') {
1976 for (var i in dialog.elements) {
1977 var elem = dialog.elements[i];
1978 if (elem.type == 'text') { x += addHtmlValue(elem.name, '<input id=cui:' + i + ' style=width:230px autocomplete=off />'); }
1979 if (elem.type == 'textarea') {
1980 if (elem.name == null) {
1981 x += '<textarea id=cui:' + i + ' style=width:356px;resize:none;height:100px autocomplete=off></textarea>';
1982 } else {
1983 x += addHtmlValue(elem.name, '<textarea id=cui:' + i + ' style=width:230px;resize:none;height:100px autocomplete=off></textarea>');
1984 }
1985 }
1986 if (elem.type == 'droplist') {
1987 var y = '';
1988 for (var j in elem.options) { y += '<option value="' + j + '">' + EscapeHtml(elem.options[j]) + '</option>'; }
1989 x += addHtmlValue(elem.name, '<select id="cui:' + i + '" style=width:230px autocomplete=off />' + y + '</select>');
1990 }
1991 }
1992 }
1993 setDialogMode(2, dialog.title, buttons, showCustomUiDialogEx, x, { action: 'uicustomevent', section: 'dialogs', element: name, src: tag, values: {}, logmsg: dialog.logmsg });
1994 }
1995
1996 // Handle a generic custom UI dialog event
1997 function showCustomUiDialogEx(b, t) {
1998 if (b != 1) return;
1999 var dialog = customui.dialogs[t.element];
2000 if (typeof dialog.elements == 'object') {
2001 for (var i in dialog.elements) {
2002 var elem = dialog.elements[i];
2003 if ((elem.type == 'droplist') || (elem.type == 'textarea') || (elem.type == 'text')) { t.values[i] = Q('cui:' + i).value; }
2004 }
2005 }
2006 meshserver.send(t);
2007 if (dialog.deselectdevices) {
2008 var elements = document.getElementsByClassName('DeviceCheckbox')
2009 for (var i = 0; i < elements.length; i++) { elements[i].checked = false; } checkedNodeids = {}; p1updateInfo();
2010 }
2011 }
2012
2013 function adjustPanels() {
2014 var hide = args.hide;
2015 if (footerBar == false) { hide |= 4; }
2016 QV('masthead', !(hide & 1));
2017 QV('topbar', !(hide & 2));
2018 QV('footer', !(hide & 4));
2019 QV('p1title', !(hide & 8));
2020 QV('p2title', !(hide & 8));
2021 QV('p3title', !(hide & 8));
2022 QV('p4title', !(hide & 8));
2023 QV('p5title', !(hide & 8));
2024 QV('p6title', !(hide & 8));
2025 QV('p10title', !(hide & 8));
2026 QV('p11title', !(hide & 8));
2027 QV('p12title', !(hide & 8));
2028 QV('p13title', !(hide & 8));
2029 QV('p14title', !(hide & 8));
2030 QV('p15title', !(hide & 8));
2031 QV('p16title', !(hide & 8));
2032 QV('p17title', !(hide & 8));
2033 QV('p18title', !(hide & 8));
2034 QV('p20title', !(hide & 8));
2035 QV('p21title', !(hide & 8));
2036 QV('p30title', !(hide & 8));
2037 QV('p31title', !(hide & 8));
2038 QV('p40title', !(hide & 8));
2039 QV('p41title', !(hide & 8));
2040 QV('p50title', !(hide & 8));
2041 QV('p51title', !(hide & 8));
2042 //if (hide & 16) { QV('page_leftbar', false); QS('page_content').left = '0px'; }
2043 if (!footerBar) { QC('body').add('nofooter'); } else { QC('body').remove('nofooter'); }
2044
2045 if (args.hide != 0) {
2046 // Fix the main grid to zero-height elements we want to hide.
2047 if (uiMode == 2) {
2048 QS('container')['grid-template-rows'] = ((hide & 1) ? '0' : '66') + 'px fit-content(48px) auto ' + ((hide & 4) ? '0' : '45') + 'px';
2049 QS('container')['-ms-grid-rows'] = ((hide & 1) ? '0' : '66') + 'px fit-content(48px) auto ' + ((hide & 4) ? '0' : '45') + 'px';
2050 } else {
2051 QS('container')['grid-template-rows'] = ((hide & 1) ? '0' : '66') + 'px ' + ((hide & 2) ? '0' : '24') + 'px auto ' + ((hide & 4) ? '0' : '45') + 'px';
2052 QS('container')['-ms-grid-rows'] = ((hide & 1) ? '0' : '66') + 'px ' + ((hide & 2) ? '0' : '24') + 'px auto ' + ((hide & 4) ? '0' : '45') + 'px';
2053 }
2054 }
2055
2056 // Adjust height of remote desktop, files and Intel AMT
2057 // 1 = Top bar, 2 = Tool Bar, 4 = Bottom Bar, 8 = Tab Title
2058 var xh = (((hide & 1) ? 0 : 66) + ((hide & 2) ? 0 : 24) + ((hide & 4) ? 0 : 45) + ((hide & 8) ? 0 : 60)); // 0 to 195
2059 var xh2 = (uiMode > 1)?24:0;
2060 QS('p2info')['height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
2061 QS('p2info')['max-height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
2062 QS('p3users')['max-height'] = 'calc(100vh - ' + (50 + xh) + 'px)'; // 124
2063 QS('p50groups')['max-height'] = 'calc(100vh - ' + (50 + xh) + 'px)'; // 124
2064 QS('p3events')['height'] = 'calc(100vh - ' + (50 + xh) + 'px)'; // 124
2065 QS('p5filetable')['height'] = 'calc(100vh - ' + (99 + xh) + 'px)'; // 160
2066 QS('p13filetable')['height'] = 'calc(100vh - ' + (127 + xh + xh2) + 'px)'; // 124
2067 QS('serverMainStats')['height'] = 'calc(100vh - ' + (50 + xh + xh2) + 'px)'; // 110
2068 QS('serverMainStats')['max-height'] = 'calc(100vh - ' + (50 + xh + xh2) + 'px)'; // 110
2069 QS('xdevices')['max-height'] = 'calc(100vh - ' + (46 + xh) + 'px)'; // 124
2070 QS('xdevicesmap')['max-height'] = 'calc(100vh - ' + (46 + xh) + 'px)'; // 124
2071 QS('p15agentConsole')['height'] = 'calc(100vh - ' + (84 + xh + xh2) + 'px)';
2072 QS('p15agentConsole')['max-height'] = 'calc(100vh - ' + (84 + xh + xh2) + 'px)';
2073 QS('p15agentConsoleText')['height'] = 'calc(100vh - ' + (81 + xh + xh2) + 'px)';
2074 QS('p15agentConsoleText')['max-height'] = 'calc(100vh - ' + (81 + xh + xh2) + 'px)';
2075 var xtermActive = !((args.xterm === 0) || ((terminal != null) && (xterm == null)));
2076 if (fullscreen) {
2077 QS('deskarea3x')['height'] = null;
2078 QS('deskarea3x')['max-height'] = null;
2079 QS('p14iframe')['height'] = null;
2080 QS('p14iframe')['max-height'] = null;
2081 if (xtermActive) {
2082 QS('termarea3x')['height'] = 'calc(100vh - 55px)';
2083 QS('termarea3xdiv')['height'] = 'calc(100vh - 55px)';
2084 QS('termarea3x')['max-width'] = 'calc(1px)';
2085 }
2086 } else {
2087 QS('deskarea3x')['height'] = 'calc(100vh - ' + (74 + xh + xh2) + 'px)';
2088 QS('deskarea3x')['max-height'] = 'calc(100vh - ' + (74 + xh + xh2) + 'px)';
2089 QS('p14iframe')['height'] = 'calc(100vh - ' + (23 + xh + xh2) + 'px)';
2090 QS('p14iframe')['max-height'] = 'calc(100vh - ' + (23 + xh + xh2) + 'px)';
2091 if (xtermActive) {
2092 QS('termarea3x')['height'] = 'calc(100vh - ' + (74 + xh + xh2) + 'px)';
2093 QS('termarea3xdiv')['height'] = 'calc(100vh - ' + (74 + xh + xh2) + 'px)';
2094 QS('termarea3x')['max-width'] = 'calc(1px)';
2095 }
2096 }
2097 QS('p43iframe')['height'] = 'calc(100vh - ' + (84 + xh) + 'px)';
2098 QS('p43iframe')['max-height'] = 'calc(100vh - ' + (84 + xh) + 'px)';
2099 QS('p6info')['height'] = 'calc(100vh - ' + (-50 + xh + xh2) + 'px)';
2100 QS('p6info')['max-height'] = 'calc(100vh - ' + (-50 + xh + xh2) + 'px)';
2101 QS('p10info')['height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
2102 QS('p10info')['max-height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
2103 QS('p17info')['height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
2104 QS('p17info')['max-height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
2105 QS('p18info')['height'] = 'calc(100vh - ' + (120 + xh + xh2) + 'px)';
2106 QS('p18info')['max-height'] = 'calc(100vh - ' + (120 + xh + xh2) + 'px)';
2107 QS('p20main')['height'] = 'calc(100vh - ' + (-50 + xh + xh2) + 'px)';
2108 QS('p20main')['max-height'] = 'calc(100vh - ' + (-50 + xh + xh2) + 'px)';
2109 QS('p21main')['height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
2110 QS('p21main')['max-height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
2111 QS('p30info')['height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
2112 QS('p30info')['max-height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
2113 QS('p51info')['height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
2114 QS('p51info')['max-height'] = 'calc(100vh - ' + (20 + xh + xh2) + 'px)';
2115 QS('p16events')['height'] = 'calc(100vh - ' + (50 + xh + xh2) + 'px)';
2116 QS('p16events')['max-height'] = 'calc(100vh - ' + (50 + xh + xh2) + 'px)';
2117 QS('p31events')['height'] = 'calc(100vh - ' + (50 + xh + xh2) + 'px)';
2118 QS('p31events')['max-height'] = 'calc(100vh - ' + (50 + xh + xh2) + 'px)';
2119 QS('p41events')['height'] = 'calc(100vh - ' + (48 + xh + xh2) + 'px)';
2120 QS('p41events')['max-height'] = 'calc(100vh - ' + (48 + xh + xh2) + 'px)';
2121 QS('p52recordings')['height'] = 'calc(100vh - ' + (48 + xh + xh2) + 'px)';
2122 QS('p52recordings')['max-height'] = 'calc(100vh - ' + (48 + xh + xh2) + 'px)';
2123 QS('p60report')['height'] = 'calc(100vh - ' + (48 + xh + xh2) + 'px)';
2124 QS('p60report')['max-height'] = 'calc(100vh - ' + (48 + xh + xh2) + 'px)';
2125
2126 // We are looking at a single device, remove all the back buttons
2127 if ((args.hide & 32) || ('{{currentNode}}'.toLowerCase() != '')) {
2128 QV('p10BackButton', false);
2129 QV('p11BackButton', false);
2130 QV('p12BackButton', false);
2131 QV('p13BackButton', false);
2132 QV('p14BackButton', false);
2133 QV('p15BackButton', false);
2134 QV('p16BackButton', false);
2135 QV('p17BackButton', false);
2136 QV('p18BackButton', false);
2137 }
2138 p1updateInfo();
2139 }
2140
2141 // Toggle the web page to full screen
2142 function toggleAspectRatio(toggle) {
2143 if (toggle === 1) { deskAspectRatio = ((deskAspectRatio + 1) % 3); putstore('deskAspectRatio', deskAspectRatio); }
2144 deskAdjust();
2145 }
2146
2147 // If FullScreen, toggle menu to be horizontal or vertical
2148 function toggleStackMenu(toggle) {
2149 if (webPageFullScreen == true) {
2150 if (toggle === 1) {
2151 webPageStackMenu = !webPageStackMenu;
2152 putstore('webPageStackMenu', webPageStackMenu);
2153 }
2154 if (webPageStackMenu == false) {
2155 QC('body').remove('menu_stack');
2156 } else {
2157 QC('body').add('menu_stack');
2158 if (xxcurrentView >= 10) QC('column_l').remove('room4submenu');
2159 }
2160 deskAdjust();
2161 }
2162 }
2163
2164 // Toggle user interface menu
2165 function showUserInterfaceSelectMenu() {
2166 if (xxdialogMode) return;
2167 Q('uiViewButton1').classList.remove('uiSelectorSel');
2168 Q('uiViewButton2').classList.remove('uiSelectorSel');
2169 Q('uiViewButton3').classList.remove('uiSelectorSel');
2170 Q('uiViewButton4').classList.remove('uiSelectorSel');
2171 Q('uiViewButton5').classList.remove('uiSelectorSel');
2172 try { Q('uiViewButton' + uiMode).classList.add('uiSelectorSel'); } catch (ex) { }
2173 QV('uiMenu', (QS('uiMenu').display == 'none'));
2174 //Q('uiViewButton1').focus();
2175 if (nightMode) { Q('uiViewButton4').classList.add('uiSelectorSel'); }
2176 if (footerBar) { Q('uiViewButton5').classList.add('uiSelectorSel'); }
2177 }
2178
2179 function userInterfaceSelectMenu(s) {
2180 if (s) { uiMode = s; putstore('uiMode', uiMode); }
2181 webPageFullScreen = (uiMode < 3);
2182 webPageStackMenu = (uiMode > 1);
2183 toggleFullScreen(0);
2184 toggleStackMenu(0);
2185 if (webPageStackMenu && (xxcurrentView >= 10)) { QC('column_l').add('room4submenu'); } else { QC('column_l').remove('room4submenu'); }
2186 adjustPanels();
2187 }
2188
2189 function toggleNightMode() {
2190 if (xxdialogMode) return;
2191 var cNightMode = getstore('nightMode', '0');
2192 var x = '<input type=radio id=night0 name=nightmoderadio value=0 ' + ((cNightMode == 0)?'checked':'') + '><label for=night0>' + "Browser default" + '</label><br>';
2193 x += '<input type=radio id=night2 name=nightmoderadio value=2 ' + ((cNightMode == 2)?'checked':'') + '><label for=night2>' + "Light mode" + '</label><br>';
2194 x += '<input type=radio id=night1 name=nightmoderadio value=1 ' + ((cNightMode == 1)?'checked':'') + '><label for=night1>' + "Dark mode" + '</label><br>';
2195 setDialogMode(2, "Night Mode", 3, toggleNightModeEx, x);
2196 QV('uiMenu', false);
2197 }
2198
2199 function toggleNightModeEx() {
2200 // Save new night mode
2201 var nNightMode = '0';
2202 if (Q('night1').checked) { nNightMode = '1'; }
2203 if (Q('night2').checked) { nNightMode = '2'; }
2204 putstore('nightMode', nNightMode);
2205 setNightMode();
2206 }
2207
2208 if(window.matchMedia) { window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', setNightMode); }
2209
2210 function setNightMode() {
2211 // Set night mode
2212 var nNightMode = getstore('nightMode', '0')
2213 nightMode = false;
2214 if ((features2 & 0x00100000) != 0) { nNightMode = '1'; }
2215 if ((features2 & 0x00200000) != 0) { nNightMode = '2'; }
2216 if (nNightMode == '1') { nightMode = true; }
2217 else if ((nNightMode == '0') && (window.matchMedia)) { nightMode = window.matchMedia('(prefers-color-scheme: dark)').matches }
2218 if (nightMode) { QC('body').add('night'); QS('body')['background-color'] = '#000'; } else { QC('body').remove('night'); QS('body')['background-color'] = '#d3d9d6'; }
2219 return nightMode;
2220 }
2221
2222 function toggleFooterBarMode() {
2223 footerBar = !footerBar;
2224 putstore('footerBar', footerBar?'1':'0');
2225 QS('container')['grid-template-rows'] = null;
2226 QS('container')['-ms-grid-rows'] = null;
2227 adjustPanels();
2228 }
2229
2230 // Toggle the web page to full screen
2231 function toggleFullScreen(toggle) {
2232 if (toggle === 1) { webPageFullScreen = !webPageFullScreen; putstore('webPageFullScreen', webPageFullScreen); }
2233 if (webPageFullScreen == false) {
2234 QC('body').remove('menu_stack');
2235 QC('body').remove('fullscreen');
2236 QC('body').remove('arg_hide');
2237 if (xxcurrentView >= 10) QC('column_l').add('room4submenu');
2238 QV('UserDummyMenuSpan', false);
2239 //QV('page_leftbar', false);
2240 } else {
2241 QC('body').add('fullscreen');
2242 if (args.hide & 16) QC('body').add('arg_hide');
2243 QV('page_leftbar', !(args.hide & 16));
2244 QV('MainMenuSpan', !(args.hide & 16));
2245 if (xxcurrentView >= 10) QC('column_l').remove('room4submenu');
2246 QV('UserDummyMenuSpan', (xxcurrentView < 10) && webPageFullScreen);
2247 }
2248 mainUpdate(512);
2249 QV('body', true);
2250 }
2251
2252 function saveUserInterfaceMode() {
2253 var nUiViewMode = 2;
2254 if (Q('ui1').checked) { nUiViewMode = 3; }
2255 if (getstore('uiViewMode', 2) != nUiViewMode) {
2256 putstore('uiViewMode', nUiViewMode);
2257 reload();
2258 }
2259 }
2260
2261 function toggleBootstrapUIMode() {
2262 if (xxdialogMode) return;
2263 var uiViewMode = getstore('uiViewMode', 2);
2264 var x = '<input type=radio id=ui0 name=uiradio value=2 ' + ((uiViewMode == 2)?'checked':'') + '><label for=ui0>' + "Classic" + '</label><br>';
2265 x += '<input type=radio id=ui1 name=uiradio value=3 ' + ((uiViewMode == 3)?'checked':'') + '><label for=ui1>' + "Modern" + '</label><br>';
2266 setDialogMode(2, "User Interface", 3, saveUserInterfaceMode, x);
2267 QV('uiMenu', false);
2268 }
2269
2270 function getNodeFromId(id) { if (nodes != null) { for (var i in nodes) { if (nodes[i]._id == id) return nodes[i]; } } return null; }
2271 function reload() {
2272 var x = window.location.href;
2273 if (x.endsWith('/#')) { x = x.substring(0, x.length - 2); }
2274 if (x.endsWith('#')) { x = x.substring(0, x.length - 1); }
2275 window.location.href = x;
2276 }
2277
2278 function onStateChanged(server, state, prevState, errorCode) {
2279 if (state == 0) {
2280 // Control web socket disconnected
2281 setDialogMode(0); // Close any dialog boxes if present
2282 go(0); // Go to disconnection panel
2283
2284 // Clean up
2285 powerTimeline = null;
2286 powerTimelineReq = null;
2287 powerTimelineNode = null;
2288 powerTimelineUpdate = null;
2289 deviceShares = null;
2290 deleteAllNotifications(); // Close and clear notifications if present
2291 hideContextMenu(); // Hide the context menu if present
2292 QV('verifyEmailId2', false);
2293 QV('logoutControl', false);
2294 if (errorCode == 'noauth') { QH('p0span', "Unable to perform authentication"); return; }
2295 if (errorCode == 'invalidorigin') { QH('p0span', "Invalid origin in HTTP request"); return; }
2296 if (prevState == 2) { if (autoReconnect) { setTimeout(serverPoll, 5000); } } else { QH('p0span', "Unable to connect web socket"); }
2297 if (authCookieRenewTimer != null) { clearInterval(authCookieRenewTimer); authCookieRenewTimer = null; }
2298 } else if (state == 2) {
2299 // Fetch list of meshes, nodes, files
2300 meshserver.send({ action: 'usergroups' });
2301 meshserver.send({ action: 'meshes' });
2302 meshserver.send({ action: 'nodes', id: '{{currentNode}}', skip: (devicePagingState == null) ? 0 : devicePagingState.skip });
2303 meshserver.send({ action: 'loginTokens' });
2304 if (pluginHandler != null) { meshserver.send({ action: 'plugins' }); }
2305 if ('{{currentNode}}'.toLowerCase() == '') { meshserver.send({ action: 'files' }); }
2306 if ('{{viewmode}}'.toLowerCase() == '') { go(1); }
2307 authCookieRenewTimer = setInterval(function () { meshserver.send({ action: 'authcookie' }); }, 1800000); // Request a cookie refresh every 30 minutes.
2308 if (xxcurrentView == 40) { refreshServerTimelineStats(); }
2309 }
2310 }
2311
2312 // Poll the server, if it responds, refresh the page.
2313 function serverPoll() {
2314 var xdr = null;
2315 try { xdr = new XDomainRequest(); } catch (e) { }
2316 if (!xdr) xdr = new XMLHttpRequest();
2317 xdr.open('HEAD', window.location.href);
2318 xdr.timeout = 15000;
2319 // Make sure there isn't a reverse proxy in front that may just be returning 5xx codes
2320 // Status code 4xx should still be allowed, since a page could potentially be removed, etc
2321 xdr.onload = function () { if (xdr.status < 500) reload(); else setTimeout(serverPoll, 10000); };
2322 xdr.onerror = xdr.ontimeout = function () { setTimeout(serverPoll, 10000); };
2323 xdr.send();
2324 }
2325
2326 // Return true if this browser is Windows based
2327 function detectWindowsBrowser() {
2328 var userAgent = window.navigator.userAgent.toUpperCase();
2329 return (userAgent.indexOf('WINDOWS') >= 0) || (userAgent.indexOf('WIN32') >= 0) || (userAgent.indexOf('WIN64') >= 0);
2330 }
2331
2332 function updateSiteAdmin() {
2333 var serverFeatures = parseInt('{{{serverfeatures}}}');
2334 var siteRights = userinfo.siteadmin;
2335 var accountSettingsLocked = ((siteRights != 0xFFFFFFFF) && ((siteRights & 1024) != 0)) || ((features2 & 0x100) != 0); // Not admin and have account features locked, or using a loginToken
2336
2337 // Update account actions
2338 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.
2339 QV('p2AccountActions', !accountSettingsLocked)
2340 QV('managePhoneNumber1', (features & 0x02000000) && (features & 0x04000000) && (serverinfo.lock2factor != true));
2341 QV('managePhoneNumber2', (features & 0x02000000) && !(features & 0x04000000) && (serverinfo.lock2factor != true));
2342 QV('manageMessaging1', (features2 & 0x02000000) && (features2 & 0x04000000) && (serverinfo.lock2factor != true));
2343 QV('manageMessaging2', (features2 & 0x02000000) && !(features2 & 0x04000000) && (serverinfo.lock2factor != true));
2344 QV('manageEmail2FA', (features & 0x00800000) && (serverinfo.lock2factor != true));
2345 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
2346 //QV('p2AccountImage', ((features & 4) == 0) && (serverinfo.domainauth == false)); // If account actions are not visible, also remove the image on that panel
2347 QV('accountCreateLoginTokenSpan', features2 & 0x00000080);
2348 QV('p2AccountImageFrame', !accountSettingsLocked)
2349 QV('p2ServerActions', (siteRights & 21) && ((serverFeatures & 143) != 0));
2350 QV('LeftMenuMyServer', (siteRights & 21) && ((serverFeatures & 64) != 0)); // 16 + 4 + 1
2351 QV('MainMenuMyServer', siteRights & 21);
2352 QV('p2ServerActionsBackup', (siteRights & 1) && ((serverFeatures & 1) != 0));
2353 QV('p2ServerActionsRestore', (siteRights & 4) && ((serverFeatures & 2) != 0));
2354 QV('p2ServerActionsVersion', (siteRights & 16) && ((serverFeatures & 4) != 0));
2355 QV('p2ServerActionsErrors', (siteRights & 16) && ((serverFeatures & 8) != 0));
2356 QV('p2ServerActionsConfig', (siteRights & 16) && ((serverFeatures & 128) != 0));
2357 QV('MainMenuMyFiles', siteRights & 8);
2358 QV('LeftMenuMyFiles', siteRights & 8);
2359 if (((siteRights & 8) == 0) && (xxcurrentView == 5)) { setDialogMode(0); go(1); }
2360 if (currentNode != null) { gotoDevice(currentNode._id, xxcurrentView, true); }
2361
2362 // Update user image
2363 if ((userinfo.flags != null) && (userinfo.flags & 1)) {
2364 if (userinfo.accountImageRnd == null) { userinfo.accountImageRnd = Math.floor(Math.random() * 9999999999); }
2365 Q('p2AccountImage').src = 'userimage.ashx?rnd=' + userinfo.accountImageRnd;
2366 } else {
2367 Q('p2AccountImage').src = 'images/user-256.png';
2368 }
2369
2370 // Update user management state
2371 if ((userinfo.siteadmin & 2) != 0) {
2372 // We are user administrator
2373 if (users == null) { meshserver.send({ action: 'users' }); }
2374 if (wssessions == null) { meshserver.send({ action: 'wssessioncount' }); }
2375 mainUpdate(8192 + 16384);
2376 } else {
2377 // We are not user administrator
2378 users = null;
2379 wssessions = null;
2380 mainUpdate(16384);
2381 if (xxcurrentView == 4 || ((xxcurrentView >= 30) && (xxcurrentView < 40))) { setDialogMode(0); go(1); currentUser = null; }
2382 }
2383 meshserver.send({ action: 'events', limit: parseInt(p3limitdropdown.value) });
2384 QV('ServerConsole', (userinfo.siteadmin === 0xFFFFFFFF) && ((serverFeatures & 16) != 0));
2385 QV('ServerTrace', (userinfo.siteadmin === 0xFFFFFFFF) && ((serverFeatures & 32) != 0));
2386 if ((xxcurrentView == 115) && (userinfo.siteadmin != 0xFFFFFFFF)) { go(6); }
2387 if ((xxcurrentView == 6) && ((userinfo.siteadmin & 21) == 0)) { go(1); }
2388
2389 // If we are site administrator, register to get server statistics
2390 if ((siteRights & 21) != 0) { meshserver.send({ action: 'serverstats', interval: 10000 }); }
2391 }
2392
2393 // To boost the speed of the web page when even floods occur, this method perform a delayed update on the web page.
2394 var updateNaggleTimer = null;
2395 var updateNaggleFlags = 0;
2396 function mainUpdate(flags) {
2397 updateNaggleFlags |= flags;
2398 if (updateNaggleTimer == null) {
2399 updateNaggleTimer = setTimeout(function () {
2400 if (updateNaggleFlags & 512) { center(); }
2401 if (updateNaggleFlags & 1) { onSearchInputChanged(); }
2402 if (updateNaggleFlags & 2) { onSortSelectChange(false); }
2403 if (updateNaggleFlags & 128) { updateMeshes(); }
2404 if (updateNaggleFlags & 4) { updateDevices(); updateDeviceDetails(); if (xxcurrentView == 21) { p21updateMesh(); } }
2405 if (updateNaggleFlags & 8) { drawNotifications(); }
2406 if ((updateNaggleFlags & 16) && (features & 0x00008000)) { updateMapMarkers(); }
2407 if (updateNaggleFlags & 32) { eventsUpdate(); }
2408 if ((updateNaggleFlags & 64) && (features & 0x00008000)) { refreshMap(false, true); }
2409 if (updateNaggleFlags & 256) { drawDeviceTimeline(); }
2410 if (updateNaggleFlags & 1024) { deviceEventsUpdate(); }
2411 if (updateNaggleFlags & 2048) { userEventsUpdate(); }
2412 if (updateNaggleFlags & 4096) { p20updateMesh(); }
2413 if (updateNaggleFlags & 8192) { updateUserGroups(); }
2414 if (updateNaggleFlags & 16384) { updateUsers(); }
2415 if (updateNaggleFlags & 32768) { updateRecordings(); }
2416 if (updateNaggleFlags & 65536) { updateLoginTokens(); }
2417 updateNaggleTimer = null;
2418 updateNaggleFlags = 0;
2419 gotoStartViewPage();
2420 }, 150);
2421 }
2422 }
2423
2424 // Return the number of 2nd factor for this account
2425 function count2factoraAuths() {
2426 if (userinfo == null) return -1;
2427 var authFactorCount = 0;
2428 if (userinfo.otpsecret == 1) { authFactorCount++; } // Authenticator time factor
2429 if (userinfo.otpduo == 1) { authFactorCount++; } // Duo factor
2430 if (userinfo.otpdev == 1) { authFactorCount++; } // Push authentication factor
2431 if (userinfo.otphkeys > 0) { authFactorCount += userinfo.otphkeys; } // FIDO hardware factor
2432 if ((features & 0x00800000) && (userinfo.otpekey == 1)) { authFactorCount++; } // EMail factor
2433 if ((features & 0x02000000) && (features & 0x04000000) && (userinfo.phone != null)) { authFactorCount++; } // SMS factor
2434 if ((features2 & 0x02000000) && (features2 & 0x04000000) && (userinfo.msghandle != null)) { authFactorCount++; } // Messaging factor
2435 if ((authFactorCount > 0) && (userinfo.otpkeys > 0) && ((features2 & 0x40000) == 0)) { authFactorCount++; } // Backup keys
2436 return authFactorCount;
2437 }
2438
2439 var backupCodesWarningDone = false;
2440 function updateSelf() {
2441 var authFactorCount = count2factoraAuths(); // Get the number of 2nd factors
2442 var accountSettingsLocked = ((userinfo.siteadmin != 0xFFFFFFFF) && ((userinfo.siteadmin & 1024) != 0));
2443 QV('verifyEmailId', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
2444 QV('verifyEmailId2', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true) && (accountSettingsLocked == false));
2445 QV('manageOtp', (serverinfo.lock2factor != true) && (authFactorCount > 0) && ((features2 & 0x40000) == 0));
2446 QV('authPhoneNumberCheck', (userinfo.phone != null));
2447 QV('authMessagingCheck', (userinfo.msghandle != null));
2448 QV('authEmailSetupCheck', (userinfo.otpekey == 1) && (userinfo.email != null) && (userinfo.emailVerified == true));
2449 QV('authDuoSetupCheck', (userinfo.otpduo == 1) && ((features2 & 0x20000000) != 0));
2450 QV('authAppSetupCheck', userinfo.otpsecret == 1);
2451 QV('manageAuthApp', (serverinfo.lock2factor != true) && ((userinfo.otpsecret == 1) || ((features2 & 0x00020000) == 0)));
2452 QV('manageDuoApp', (serverinfo.lock2factor != true) && ((features2 & 0x20000000) != 0));
2453 QV('authKeySetupCheck', userinfo.otphkeys > 0);
2454 QV('authPushAuthDevCheck', (userinfo.otpdev > 0) && ((features2 & 0x40) != 0));
2455 QV('authCodesSetupCheck', userinfo.otpkeys > 0);
2456 QV('managePushAuthDev', (serverinfo.lock2factor != true) && (features2 & 0x40) && (authFactorCount > 0));
2457 QV('manageHardwareOtp', (serverinfo.lock2factor != true));
2458 mainUpdate(4 + 128 + 4096);
2459
2460 // Check if none or at least 2 factors are enabled.
2461 if ((backupCodesWarningDone == false) && (authFactorCount == 1) && ((features2 & 0x80000) == 0)) {
2462 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' });
2463 backupCodesWarningDone = true;
2464 }
2465
2466 // If we can't create new groups, hide all links that can do that.
2467 var newGroupsAllowed = ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 64) == 0));
2468 QV('p2createMeshLink1', newGroupsAllowed);
2469 QV('p2createMeshLink2', newGroupsAllowed);
2470 QV('getStarted1', newGroupsAllowed);
2471 QV('getStarted2', !newGroupsAllowed);
2472
2473 if (typeof userinfo.passchange == 'number') {
2474 if (userinfo.passchange == -1) { QH('p2nextPasswordUpdateTime', " - Reset on next login."); }
2475 else if ((passRequirements != null) && (typeof passRequirements.reset == 'number')) {
2476 var seconds = (userinfo.passchange) + (passRequirements.reset * 86400) - Math.floor(Date.now() / 1000);
2477 if (seconds < 0) { QH('p2nextPasswordUpdateTime', " - Reset on next login."); }
2478 else if (seconds < 3600) { var secs = Math.floor(seconds / 60); QH('p2nextPasswordUpdateTime',format((secs == 1)?" - Reset in 1 minute.":" - Reset in {0} minutes.", secs)); }
2479 else if (seconds < 86400) { var hours = Math.floor(seconds / 3600); QH('p2nextPasswordUpdateTime', format((hours == 1) ? " - Reset in 1 hour." : " - Reset in {0} hours.", hours)); }
2480 else { var days = Math.floor(seconds / 86400); QH('p2nextPasswordUpdateTime', format((hours == 1) ? " - Reset in 1 day." : " - Reset in {0} days.", days)); }
2481 }
2482 }
2483
2484 // Adjust "My Users" tabs
2485 QV('MainMenuMyUsers', ((users != null) && ((features & 4) == 0)) || (((userinfo.siteadmin & 512) != 0) && ((features & 0x08000000) != 0)));
2486 QV('LeftMenuMyUsers', ((users != null) && ((features & 4) == 0)) || (((userinfo.siteadmin & 512) != 0) && ((features & 0x08000000) != 0)));
2487 QV('UsersGeneral', ((users != null) && ((features & 4) == 0)));
2488 QV('UsersGroups', ((users != null) && ((features & 4) == 0)));
2489 QV('UsersRecordings', ((userinfo.siteadmin & 512) != 0) && ((features & 0x08000000) != 0));
2490 }
2491
2492 function setSessionActivity() { sessionActivity = Date.now(); QH('idleTimeoutNotify', ''); }
2493 function checkIdleSessionTimeout() {
2494 var delta = (Date.now() - sessionActivity);
2495 if (delta > serverinfo.timeout) {
2496 if (desktop != null) { // Disconnect remote desktop
2497 desktop.Stop();
2498 webRtcDesktopReset();
2499 desktopNode = desktop = null;
2500 if (pluginHandler != null) { pluginHandler.callHook('onDesktopDisconnect'); }
2501 }
2502 if (terminal != null) { // Disconnect terminal
2503 terminal.Stop();
2504 terminal = null;
2505 }
2506 if (files != null) { // Disconnect files
2507 files.Stop();
2508 files = null;
2509 }
2510 if (serverinfo.logoutonidlesessiontimeout) {
2511 if (urlargs.key) { window.location.href = 'logout?key=' + urlargs.key; }
2512 else { window.location.href = 'logout'; }
2513 }
2514 } else {
2515 var ds = Math.round((serverinfo.timeout - delta) / 1000);
2516 var sessionInProgress = desktop != null || terminal != null || files != null;
2517 var show = serverinfo.logoutonidlesessiontimeout || sessionInProgress;
2518 var isLogout = serverinfo.logoutonidlesessiontimeout;
2519 var theText = ''; // Initialize theText
2520 if (ds <= 60) {
2521 theText = isLogout
2522 ? (ds == 1 ? "1 second until logout" : "{0} seconds until logout")
2523 : (ds == 1 ? "1 second until disconnect" : "{0} seconds until disconnect");
2524 } else {
2525 ds = Math.round(ds / 60);
2526 if (ds <= 5) {
2527 theText = isLogout
2528 ? (ds == 1 ? "1 minute until logout" : "{0} minutes until logout")
2529 : (ds == 1 ? "1 minute until disconnect" : "{0} minutes until disconnect");
2530 }
2531 }
2532 QH('idleTimeoutNotify', show && theText ? '<br />' + format(theText, ds) : '');
2533 }
2534 }
2535
2536 function onMessage(server, message) {
2537 switch (message.action) {
2538 case 'trace': {
2539 serverTrace.unshift(message);
2540 displayServerTrace();
2541 break;
2542 }
2543 case 'intersession' : {
2544 if (message.subaction == 'removeNotify') { notificationDelete(message.id); }
2545 break;
2546 }
2547 case 'traceinfo': {
2548 if (typeof message.traceSources == 'object') {
2549 if ((message.traceSources != null) && (message.traceSources.length > 0)) {
2550 serverTraceSources = message.traceSources;
2551 QH('p41traceStatus', EscapeHtml(message.traceSources.join(', ')));
2552 } else {
2553 serverTraceSources = [];
2554 QH('p41traceStatus', "None");
2555 }
2556 }
2557 break;
2558 }
2559 case 'serverstats': {
2560 updateGeneralServerStats(message);
2561 break;
2562 }
2563 case 'serverwarnings': {
2564 if ((message.warnings != null) && (message.warnings.length > 0)) {
2565 var ServerWarnings = {
2566 1: "",
2567 2: "Missing WebDAV parameters.",
2568 3: "Unrecognized configuration option \"{0}\".",
2569 4: "WebSocket compression is disabled, this feature is broken in NodeJS v11.11 to v12.15 and v13.2",
2570 5: "Unable to load Intel AMT TLS root certificate for default domain.",
2571 6: "Unable to load Intel AMT TLS root certificate for domain {0}.",
2572 7: "CIRA local FQDN's ignored when server in LAN-only or WAN-only mode.",
2573 8: "Can't have more than 4 CIRA local FQDN's. Ignoring value.",
2574 9: "Agent hash checking is being skipped, this is unsafe.",
2575 10: "Missing Let's Encrypt email address.",
2576 11: "Invalid Let's Encrypt host names.",
2577 12: "Invalid Let's Encrypt names, can't contain a *.",
2578 13: "Unable to setup Let's Encrypt module.",
2579 14: "Invalid Let's Encrypt names, unable to resolve: {0}",
2580 15: "Invalid Let's Encrypt email address, unable to resolve: {0}",
2581 16: "Unable to load CloudFlare trusted proxy IPv6 address list.",
2582 17: "SendGrid server has limited use in LAN mode.",
2583 18: "SMTP server has limited use in LAN mode.",
2584 19: "SMS gateway has limited use in LAN mode.",
2585 20: "Invalid \"LoginCookieEncryptionKey\" in config.json.",
2586 21: "Backup path can't be set within meshcentral-data folder, backup settings ignored.",
2587 22: "Failed to sign agent {0}: {1}",
2588 23: "Unable to load agent icon file: {0}.",
2589 24: "Unable to load agent logo file: {0}.",
2590 25: "This NodeJS version does not support OpenID.",
2591 26: "This NodeJS version does not support Discord.js.",
2592 27: "Firebase now requires a service account JSON file, Firebase disabled."
2593 };
2594 var x = '';
2595 for (var i in message.warnings) {
2596 var y = message.warnings[i];
2597 if (typeof y == 'string') {
2598 x += '<div style=color:red;padding-bottom:6px><b>' + "WARNING: " + y + '</b></div>';
2599 } else {
2600 var z = ServerWarnings[y.id];
2601 if (z == null) { z = y.msg; } else { if (y.args != null) { z = format(z, ...y.args); } }
2602 x += '<div style=color:red;padding-bottom:6px><b>' + "WARNING: " + z + '</b></div>';
2603 }
2604 }
2605 QH('serverWarnings', x);
2606 QV('serverWarningsDiv', true);
2607 }
2608 break;
2609 }
2610 case 'servertimelinestats': {
2611 setServerTimelineStats(message.events);
2612 break;
2613 }
2614 case 'authcookie': {
2615 // Got an authentication cookie refresh
2616 authCookie = message.cookie;
2617 authRelayCookie = message.rcookie;
2618 break;
2619 }
2620 case 'serverinfo': {
2621 serverinfo = message.serverinfo;
2622 if (serverinfo.timeout) { setInterval(checkIdleSessionTimeout, 10000); checkIdleSessionTimeout(); }
2623 if (debugmode == 1) { console.log('Server time: ', printDateTime(new Date(serverinfo.serverTime))); }
2624 setupServiceWorker();
2625 if (serverinfo.certExpire != null) {
2626 var days = Math.floor((serverinfo.certExpire - Date.now()) / 86400000);
2627 if ((days >= 0) && (days < 20)) {
2628 QH('serverCertWarnings', '<div style=color:red;padding-bottom:6px><b>' + "WARNING: " + format("Certificate expires in {0} day(s)", days) + '</b></div>');
2629 QV('serverWarningsDiv', true);
2630 addNotification({ text: format("Certificate expires in {0} day(s)", days) });
2631 }
2632 }
2633 if (serverinfo.preConfiguredRemoteInput) {
2634 // Setup the pre-configured keyboard remote input strings
2635 var x = '';
2636 for (var i in serverinfo.preConfiguredRemoteInput) { x += '<div class="cmtext" onclick="cmdeskpreconfigtypeaction(' + i + ',event)">' + EscapeHtml(serverinfo.preConfiguredRemoteInput[i].name) + '</div>'; }
2637 if (x != '') { x += '<hr />'; }
2638 QH('deskPreConfigShortcutContextMenu1', x);
2639 }
2640 if (serverinfo.preConfiguredScripts) {
2641 // Setup the pre-configured scripts
2642 var x = '', y = '';
2643 for (var i in serverinfo.preConfiguredScripts) {
2644 var s = serverinfo.preConfiguredScripts[i];
2645 var z = '<div class="cmtext" onclick="cmdeskpreconfigscriptaction(' + i + ',event)">' + EscapeHtml(serverinfo.preConfiguredScripts[i].name) + '</div>';
2646 if (s.type != 3) { x += z; }
2647 if (s.type >= 3) { y += z; }
2648 }
2649 QH('deskPreConfigScriptContextMenu1', x); // Windows devices
2650 QH('deskPreConfigScriptContextMenu2', y); // Other devices
2651 }
2652 break;
2653 }
2654 case 'userinfo': {
2655 userinfo = message.userinfo;
2656 updateSiteAdmin();
2657 updateSelf();
2658 break;
2659 }
2660 case 'users': {
2661 users = {};
2662 for (var m in message.users) { users[message.users[m]._id] = message.users[m]; }
2663 if (currentUser != null) { currentUser = users[currentUser._id]; }
2664 mainUpdate(16384);
2665 updateSelf();
2666 break;
2667 }
2668 case 'wssessioncount': {
2669 wssessions = message.wssessions;
2670 mainUpdate(16384);
2671 break;
2672 }
2673 case 'meshes': {
2674 meshes = {};
2675 for (var m in message.meshes) { meshes[message.meshes[m]._id] = message.meshes[m]; }
2676 if (currentMesh != null) { currentMesh = meshes[currentMesh._id]; }
2677 mainUpdate(4 + 128);
2678 break;
2679 }
2680 case 'usergroups': {
2681 var groupCount = 0;
2682 if (Array.isArray(message.ugroups)) {
2683 usergroups = {};
2684 for (var i in message.ugroups) { groupCount++; usergroups[message.ugroups[i]._id] = message.ugroups[i]; }
2685 if (groupCount == 0) { usergroups = null; }
2686 } else {
2687 usergroups = message.ugroups;
2688 for (var i in message.ugroups) { groupCount++; message.ugroups[i]._id = i; }
2689 if (groupCount == 0) { usergroups = null; }
2690 }
2691 mainUpdate(8192);
2692 break;
2693 }
2694 case 'files': {
2695 filetree = setupBackPointers(message.filetree);
2696 updateFiles();
2697 d3updatefiles();
2698 break;
2699 }
2700 case 'nodes': {
2701 nodes = [];
2702 for (var m in message.nodes) {
2703 for (var n in message.nodes[m]) {
2704 if (message.nodes[m][n]._id == null) { console.log('Invalid node (' + n + '): ' + JSON.stringify(message.nodes)); continue; }
2705 if (message.nodes[m][n].name == null) { message.nodes[m][n].name = '???'; }
2706 message.nodes[m][n].namel = message.nodes[m][n].name.toLowerCase();
2707 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; }
2708 message.nodes[m][n].meshnamel = meshes[m]?meshes[m].name.toLowerCase():'*';
2709 message.nodes[m][n].meshid = m;
2710 message.nodes[m][n].state = (message.nodes[m][n].state)?(message.nodes[m][n].state):0;
2711 if (!message.nodes[m][n].icon) message.nodes[m][n].icon = 1;
2712 message.nodes[m][n].ident = ++nodeShortIdent;
2713 nodes.push(message.nodes[m][n]);
2714 }
2715 }
2716
2717 // Remove any stars for nodes that don't exist
2718 for (var i in stars) { if (getNodeFromId(i) == null) { delete stars[i]; } }
2719
2720 // If we are currently looking at a node this is now gone, change the view.
2721 if ((currentNode != null) && (IsNodeViewable(currentNode) == false)) { currentNode = null; go(1); }
2722
2723 // Change the reference to the current node
2724 if (currentNode != null) { currentNode = getNodeFromId(currentNode._id); if (currentNode != null) { gotoDevice(currentNode._id, xxcurrentView, true); } else { go(1); } }
2725
2726 // Update device paging
2727 devicePagingState = (message.totalcount == null) ? null : { total: message.totalcount, skip: message.skip, limit: message.limit };
2728 updateDevicePageState();
2729
2730 mainUpdate(1 | 2 | 4 | 64);
2731
2732 // If groups are to be collapsed by default, do it now
2733 if (collapseGroups === 'true' && Object.keys(CollapsedGroups).length === 0 && typeof(getstore('_collapse')) === 'undefined') { cmexpandaction(2); }
2734
2735 break;
2736 }
2737 case 'powertimeline': {
2738 if (message.nodeid != powerTimelineReq) break;
2739 powerTimelineNode = message.nodeid;
2740 powerTimeline = message.timeline;
2741 powerTimelineUpdate = Date.now() + 300000; // Update every 5 minutes
2742 for (var i in powerTimeline) { if (i % 2 == 1) { powerTimeline[i] = powerTimeline[i] * 1000; } } // Decompress time
2743 if (currentNode._id == message.nodeid) { mainUpdate(256); }
2744 break;
2745 }
2746 case 'deviceShares': {
2747 if (message.nodeid != deviceSharesReq) break;
2748 deviceSharesNode = message.nodeid;
2749 deviceShares = message.deviceShares;
2750 if ((currentNode != null) && (currentNode._id == message.nodeid)) { gotoDevice(currentNode._id, xxcurrentView, true); }
2751 break;
2752 }
2753 case 'deviceMeshShares': {
2754 if (message.meshid != deviceSharesReq) break;
2755 deviceSharesNode = message.meshid;
2756 deviceShares = message.deviceShares;
2757 if ((currentMesh != null) && (currentMesh._id == message.meshid)) { p20updateMesh(); }
2758 break;
2759 }
2760 case 'getsysinfo': {
2761 if (message.nodeid != powerTimelineReq) break;
2762 if (message.noinfo === true) {
2763 updateDeviceDetails(getNodeFromId(message.nodeid));
2764 } else {
2765 message.hardware.time = message.time;
2766 updateDeviceDetails(getNodeFromId(message.nodeid), message.hardware);
2767 }
2768 break;
2769 }
2770 case 'software': {
2771 try {
2772 var softwareData = message.value;
2773 if (JSON.parse(message.value).error) {
2774 QH('p18Status', '<span style=color:red>' + "ERROR: " + JSON.parse(message.value).error + '</span>');
2775 QH('p18html', '<div style="text-align:center;padding:50px;color:gray"><i>' + "Click Refresh to load installed software..." + '</i></div>');
2776 installedAppsLoading = false;
2777 installedAppsLoadingType = 0;
2778 return;
2779 }
2780 if (typeof softwareData === 'string' && softwareData.trim().charAt(0) === '[' && softwareData.indexOf('"name"') >= 0) {
2781 handleInstalledAppsResponse(softwareData);
2782 } else if (typeof softwareData === 'string' && (softwareData.indexOf('"success"') >= 0 || softwareData.indexOf('"status"') >= 0 || softwareData.indexOf('"error"') >= 0)) {
2783 handleUninstallResponse(softwareData);
2784 } else {
2785 installedAppsLoading = false;
2786 installedAppsLoadingType = 0;
2787 displayInstalledApps();
2788 }
2789 } catch (xx) { }
2790 break;
2791 }
2792 case 'lastconnect': {
2793 var node = getNodeFromId(message.nodeid);
2794 if (node != null) {
2795 node.lastconnect = message.time;
2796 node.lastaddr = message.addr;
2797 if ((currentNode._id == node._id) && (Q('MainComputerState').innerHTML == '') && (node.lastconnect != null)) {
2798 QH('MainComputerState', '<span>' + "Last seen:" + '<br />' + printDateTime(new Date(node.lastconnect)) + '</span>');
2799 }
2800 }
2801 break;
2802 }
2803 case 'lastconnects': {
2804 var lcnodes = Object.keys(message.lastconnects);
2805 for (var i in lcnodes) {
2806 var lcnodeid = lcnodes[i];
2807 var node = getNodeFromId(lcnodeid);
2808 if (node != null) { node.lastconnect = message.lastconnects[lcnodeid] }
2809 }
2810 mainUpdate(4);
2811 break;
2812 }
2813 case 'msg': {
2814 // Check if this is a message from a node
2815 if (message.nodeid != null) {
2816 var index = -1;
2817 if (nodes != null) { for (var i in nodes) { if (nodes[i]._id == message.nodeid) { index = i; break; } } }
2818 if (index != -1) {
2819 // Node was found, dispatch the message
2820 if ((message.type === 'cpuinfo') && (currentNode != null) && (currentNode._id == message.nodeid)) {
2821 var now = (Date.now() / 1000), cpu = 0, memory = 0;
2822 if (typeof message.cpu.total == 'number') { cpu = message.cpu.total; }
2823 if (typeof message.memory.percentConsumed == 'number') { memory = message.memory.percentConsumed; }
2824 deviceDetailsStatsData.push([now, cpu, memory]);
2825 deviceDetailsStatsDraw(message);
2826 } else if (message.type === 'console') { p15consoleReceive(nodes[index], message.value, message.source); } // This is a console message.
2827 else if (message.type === 'notify') { // This is a notification message.
2828 var n = getstore('notifications', 0);
2829 if (((n & 8) == 0) && (message.amtMessage != null)) { break; } // Intel AMT desktop & terminal messages should be ignored.
2830 var n = { text: message.value, title: message.title, icon: message.icon, titleid: message.titleid, msgid: message.msgid, args: message.args };
2831 if (message.id != null) { n.id = message.id; }
2832 if (message.nodeid != null) { n.nodeid = message.nodeid; }
2833 if (message.tag != null) { n.tag = message.tag; }
2834 if (message.url != null) { n.url = message.url; }
2835 if (message.username != null) { n.username = message.username; }
2836 if (typeof message.maxtime === 'number') { n.maxtime = message.maxtime; }
2837 addNotification(n);
2838 } else if (message.type === 'ps') {
2839 showDeskToolsProcesses(message);
2840 } else if (message.type === 'services') {
2841 showDeskToolsServices(message);
2842 } else if (message.type === 'service') {
2843 showServiceDetailsDialog(message);
2844 } else if ((message.type === 'getclip') && (currentNode != null) && (currentNode._id == message.nodeid)) {
2845 if ((message.tag == 1) && (xxdialogTag === 'clipboard')) {
2846 Q('d2clipText').value = message.data; // Put remote clipboard data into dialog box
2847 } else if ((message.tag === 2) || (message.tag === 3)) {
2848 // tag 2 = manual clipboard-in button, tag 3 = auto-clipboard poll
2849 if ((navigator.clipboard != null) && (message.data != deskLastClipboardReceived) && document.hasFocus()) {
2850 deskLastClipboardReceived = message.data;
2851 deskLastClipboardSent = message.data; // prevent echo back to remote
2852 navigator.clipboard.writeText(message.data).then(function() { }).catch(function(err) { });
2853 }
2854 }
2855 } else if ((message.type === 'setclip') && (xxdialogTag === 'clipboard') && (currentNode != null) && (currentNode._id == message.nodeid)) {
2856 // Display success/fail on the clipboard dialog box.
2857 QH('dlgClipStatus', message.success ? '<span style=color:green>' + "Success" + '</span>' : '<span style=color:red>' + "Failed" + '</span>')
2858 setTimeout(function () { try { QH('dlgClipStatus', ''); } catch (ex) { } }, 2000);
2859 } else if ((message.type === 'userSessions') && (currentNode != null) && (currentNode._id === message.nodeid) && (desktop == null)) {
2860 // Got list of user sessions
2861 var userSessions = [];
2862 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]); } } }
2863 if (userSessions.length === 0) { connectDesktop(null, 1, null, message.tag); } // No active sessions, do a normal connection.
2864 else if (userSessions.length === 1) { connectDesktop(null, 1, userSessions[0].SessionId, message.tag); } // One active session, connect to it
2865 else {
2866 var x = '';
2867 var sortBy = "{{{userSessionsSort}}}";
2868 if (sortBy != '') {
2869 userSessions.sort(function(a, b) {
2870 if (!a[sortBy]) return -1; // a comes before b
2871 if (!b[sortBy]) return 1; // b comes before a
2872 if (a[sortBy] < b[sortBy]) return -1;
2873 if (a[sortBy] > b[sortBy]) return 1;
2874 return 0;
2875 });
2876 }
2877 for (var i in userSessions) {
2878 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) : '');
2879 if (userSessions[i].Username) { if (userSessions[i].Domain) { x += ' - ' + userSessions[i].Domain + '/' + userSessions[i].Username; } else { x += ' - ' + userSessions[i].Username; } }
2880 x += '</div>';
2881 }
2882 QH('p11DeskSessionSelector', x);
2883 QV('p11DeskSessionSelector', true);
2884 }
2885 } else if (message.type === 'psinfo') {
2886 if (xxdialogTag === ('ps|' + message.nodeid + '|' + message.pid)) {
2887 var x = '<div style=max-height:200px;overflow-y:auto>';
2888 //x += addHtmlValue4("Process ID", message.pid);
2889 if ((typeof message.value == 'object') && (Object.keys(message.value).length > 0)) {
2890 // lets fix agent psinfo not being camelcase
2891 message.value = jsonToCamel(message.value);
2892 if(!message.value.cmd && message.value.path){ message.value.cmd = message.value.path }
2893 if(!message.value.processUser && message.value.userName){ message.value.processUser = message.value.userName.split("\\")[1]; }
2894 if(!message.value.processDomain && message.value.userName){ message.value.processDomain = message.value.userName.split("\\")[0]; }
2895 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>'; }
2896 if (message.value.machineName) { x += addHtmlValue5("Machine Name", message.value.machineName); }
2897 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>'; }
2898 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>'; }
2899 if (message.value.processUser) { x += addHtmlValue5("User", message.value.processUser); }
2900 if (message.value.processDomain) { x += addHtmlValue5("Domain", message.value.processDomain); }
2901 if (message.value.startTime) { x += addHtmlValue5("Start Time", message.value.startTime); }
2902 x += '<hr />';
2903 if (message.value.PriorityBoostEnabled) { x += addHtmlValue5("Priority Boost", message.value.PriorityBoostEnabled?"Enabled":"Disabled"); }
2904 if (message.value.sessionId) { x += addHtmlValue5("Session Id", message.value.sessionId); }
2905 if (message.value.handleCount) { x += addHtmlValue5("Handle Count", message.value.handleCount); }
2906 if (message.value.privilegedProcessorTime) { x += addHtmlValue5("Privileged Processor Time", format("{0} seconds", message.value.privilegedProcessorTime)); }
2907 if (message.value.totalProcessorTime) { x += addHtmlValue5("Total Processor Time", format("{0} seconds", message.value.totalProcessorTime)); }
2908 if (message.value.userProcessorTime) { x += addHtmlValue5("User Processor Time", format("{0} seconds", message.value.userProcessorTime)); }
2909 if (message.value.nonpagedSystemMemorySize) { x += addHtmlValue5("Non Paged Memory", getNiceSize2(message.value.nonpagedSystemMemorySize)); }
2910 if (message.value.pagedMemorySize) { x += addHtmlValue5("Paged Memory", getNiceSize2(message.value.pagedMemorySize)); }
2911 if (message.value.privateMemorySize) { x += addHtmlValue5("Private Memory", getNiceSize2(message.value.privateMemorySize)); }
2912 if (message.value.virtualMemorySize) { x += addHtmlValue5("Virtual Memory", getNiceSize2(message.value.virtualMemorySize)); }
2913 if (message.value.workingSet) { x += addHtmlValue5("Working Set", getNiceSize2(message.value.workingSet)); }
2914 if (message.value.peakWorkingSet) { x += addHtmlValue5("Peak Working Set", getNiceSize2(message.value.peakWorkingSet)); }
2915 if (message.value.peakPagedMemorySize) { x += addHtmlValue5("Peak Paged Memory", getNiceSize2(message.value.peakPagedMemorySize)); }
2916 if (message.value.peakVirtualMemorySize) { x += addHtmlValue5("Peak Virtual Memory", getNiceSize2(message.value.peakVirtualMemorySize)); }
2917 } else {
2918 x += "No information provided";
2919 }
2920 x += '</div>';
2921 QH('id_dialogOptions', x);
2922 }
2923 } else if (message.type === 'deskBackground') {
2924 if(message.data != "") {
2925 Q('DeskBackgroundButtonImage').src = 'images/icon-background.png';
2926 }else{
2927 Q('DeskBackgroundButtonImage').src = 'images/icon-background-red.png';
2928 }
2929 }
2930 }
2931 } else {
2932 if (message.type === 'notify') { // This is a notification message.
2933 var n = { text: message.value, title: message.title, icon: message.icon, titleid: message.titleid, msgid: message.msgid, args: message.args };
2934 if (message.id != null) { n.id = message.id; }
2935 if (message.tag != null) { n.tag = message.tag; }
2936 if (message.url != null) { n.url = message.url; }
2937 if (message.username != null) { n.username = message.username; }
2938 if (typeof message.maxtime == 'number') { n.maxtime = message.maxtime; }
2939 addNotification(n);
2940 }
2941 }
2942 break;
2943 }
2944 case 'getnetworkinfo': {
2945 if (currentNode._id != message.nodeid) return;
2946 updateDeviceDetails(getNodeFromId(message.nodeid), null, message);
2947 if ((xxdialogMode == 2) && (xxdialogTag == 'if' + message.nodeid)) {
2948 var x = '<div class=dialogText>';
2949 if (currentNode.firstconnect) { x += addHtmlValue2("First agent connection", printDateTime(new Date(currentNode.firstconnect))); }
2950 if (currentNode.lastconnect) { x += addHtmlValue2("Last agent connection", printDateTime(new Date(currentNode.lastconnect))); }
2951 if (currentNode.lastaddr) {
2952 var splitip = currentNode.lastaddr.split(':');
2953 if (splitip.length > 2) {
2954 // IPv6
2955 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>');
2956 } else {
2957 // IPv4
2958 if (isPrivateIP(currentNode.lastaddr)) {
2959 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>');
2960 } else {
2961 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>');
2962 }
2963 }
2964 }
2965 if (message.updateTime != null) { x += addHtmlValue2("Last interfaces update", printDateTime(new Date(message.updateTime))); }
2966
2967 if (message.netif != null) {
2968 // Old style
2969 for (var i in message.netif) {
2970 var net = message.netif[i];
2971 x += '<hr />'
2972 if (net.name) { x += addHtmlValue2("Name", '<b>' + EscapeHtml(net.name) + '</b>'); }
2973 if (net.desc) { x += addHtmlValue2("Description", EscapeHtml(net.desc).replace('(R)', '&reg;').replace('(r)', '&reg;')); }
2974 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>'); }
2975 if (net.mac) { x += addHtmlValue2("MAC address", '<a href="https://maclookup.app/search/result?mac=' + 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>'); }
2976 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>'); }
2977 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>'); }
2978 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>'); }
2979 if (net.gatewaymac) { x += addHtmlValue2("Gateway MAC", '<a href="https://maclookup.app/search/result?mac=' + 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>'); }
2980 }
2981 } else if (message.netif2 != null) {
2982 // New style
2983 for (var i in message.netif2) {
2984 var net = message.netif2[i];
2985 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;
2986 x += '<hr />'
2987 x += addHtmlValue2("Name", '<b>' + EscapeHtml(i) + '</b>');
2988 if (typeof net[0].mac == 'string') { x += addHtmlValue2("MAC address", '<a href="https://maclookup.app/search/result?mac=' + 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>'); }
2989 if (net[0].fqdn) { x += addHtmlValue2("FQDN", net[0].fqdn); }
2990 for (var j = 0; j < net.length; j++) {
2991 var netif = net[j];
2992 if (netif.family == 'IPv6') {
2993 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>'); }
2994 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>'); }
2995 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>'); }
2996 } else if (netif.family == 'IPv4') {
2997 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>'); }
2998 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>'); }
2999 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>'); }
3000 }
3001 }
3002 }
3003 } else {
3004 x += '<hr />'
3005 x += "No network interface information available for this device.";
3006 }
3007 x += '</div>';
3008 QH('d2netinfo', x);
3009 }
3010 break;
3011 }
3012 case 'serverversion': {
3013 if ((xxdialogMode == 2) && (xxdialogTag == 'MeshCentralServerUpdate')) {
3014 var x = '<div class=dialogText>';
3015 if (!message.tags.current) { message.tags.current = "Unknown"; }
3016 if (!message.tags.stable) { message.tags.stable = "Unknown"; }
3017 if (!message.tags.latest) { message.tags.latest = "Unknown"; }
3018 x += addHtmlValue2("Current Version", '<b>' + EscapeHtml(message.tags.current) + '</b>');
3019 x += '<hr />';
3020 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>');
3021 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>');
3022 x += '</div>';
3023 if (((message.tags.current == message.tags.latest) && (message.tags.current == message.tags.stable)) || ((features & 2048) == 0)) {
3024 setDialogMode(2, "MeshCentral Version", 1, null, x);
3025 } else {
3026 x += '<hr />' + "Check and click OK to start server self-update.";
3027 setDialogMode(2, "MeshCentral Version", 3, server_showVersionDlgEx, x, message.tags);
3028 server_showVersionDlgUpdate();
3029 }
3030 }
3031 break;
3032 }
3033 case 'servererrors': {
3034 if ((xxdialogMode == 2) && (xxdialogTag == 'MeshCentralServerErrors')) {
3035 if (message.data == null) {
3036 setDialogMode(2, "Server Errors", 1, null, "Server has no error log.");
3037 } else {
3038 var x = '<div class="dialogText dialogTextLog"><pre id=d2ServerErrorsLogPre>' + EscapeHtml(message.data) + '</pre></div>';
3039 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>';
3040 x += '<div><label><input id=d2clearErrorsCheck type=checkbox onclick=server_showErrorsDlgUpdate() /> ' + "Check and click OK to clear error log." + '</label></div>';
3041 setDialogMode(2, "MeshCentral Server Errors", 3, server_showErrorsDlgEx, x);
3042 server_showErrorsDlgUpdate();
3043 }
3044 }
3045 break;
3046 }
3047 case 'serverconfig': {
3048 if ((xxdialogMode == 2) && (xxdialogTag == 'MeshCentralServerConfig')) {
3049 if (message.data == null) {
3050 setDialogMode(2, "Server Configuration", 1, null, "Server has no config file.");
3051 } else {
3052 setDialogMode(4, "Server Configuration", 2);
3053 QV('d4EncodingButton', false);
3054 QV('d4LineBreakButton', false);
3055 QS('dialog').width = 'auto';
3056 QS('dialog').bottom = '80px';
3057 QS('dialog').top = QS('dialog').left = QS('dialog').right = '100px';
3058 Q('d4editorarea').value = message.data;
3059 Q('d4editorarea').setAttribute('readonly','readonly');
3060 }
3061 }
3062 break;
3063 }
3064 case 'serverconsole': {
3065 p15consoleReceive('serverconsole', message.value);
3066 break;
3067 }
3068 case 'events': {
3069 if ((message.nodeid != null) && (currentNode != null) && (message.nodeid == currentNode._id)) {
3070 currentDeviceEvents = message.events;
3071 mainUpdate(1024);
3072 } else if ((message.userid != null) && (currentUser != null) && (message.userid == currentUser._id)) {
3073 currentUserEvents = message.events;
3074 mainUpdate(2048);
3075 } else {
3076 events = message.events;
3077 mainUpdate(32);
3078 }
3079 break;
3080 }
3081 case 'recordings': {
3082 p52recordings = message.events;
3083 if (message.error != null) { p52recordings = message.error; }
3084 updateRecordings();
3085 break;
3086 }
3087 case 'getcookie': {
3088 if (message.tag == 'MCRouter') {
3089 var servername = serverinfo.name;
3090 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.
3091 var domainUrlNoSlash = domainUrl.substring(0, domainUrl.length - 1);
3092 var portStr = (serverinfo.port == 443) ? '' : (':' + serverinfo.port);
3093 var url = 'mcrouter://' + servername + portStr + domainUrl + 'control.ashx?c=' + authCookie + '&t=' + serverinfo.tlshash + '&l={{{lang}}}' + (urlargs.key?('&key=' + urlargs.key):'');
3094 if (message.nodeid != null) { url += ('&nodeid=' + message.nodeid); }
3095 if (message.tcpport != null) { url += ('&protocol=1&remoteport=' + message.tcpport); }
3096 if (message.localRelay) { url += '&local=1'; }
3097 if (message.localport) { url += '&localport=' + message.localport; }
3098 if (message.name != null) { url += ('&name=' + encodeURIComponentEx(message.name)); }
3099 if (message.ip != null) { url += ('&remoteip=' + message.ip); }
3100 url += ('&appid=' + message.protocol + '&autoexit=1'); // Protocol: 0 = Custom, 1 = HTTP, 2 = HTTPS, 3 = RDP, 4 = PuTTY, 5 = WinSCP, 6 = MCRDesktop, 7 = MCRFiles
3101 //console.log(url);
3102 downloadFile(url, '');
3103 } else if (message.tag == 'novnc') {
3104 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}}}';
3105 var node = getNodeFromId(message.nodeid);
3106 if (node != null) { vncurl += '&name=' + encodeURIComponentEx(node.name); }
3107 safeNewWindow(vncurl, 'mcnovnc/' + message.nodeid);
3108 } else if (message.tag == 'mstsc') {
3109 var rdpurl = window.location.origin + domainUrl + 'mstsc.html?ws=' + message.cookie + (urlargs.key?('&key=' + urlargs.key):'');
3110 var node = getNodeFromId(message.nodeid);
3111 if (node != null) { rdpurl += '&name=' + encodeURIComponentEx(node.name); }
3112 if (message.localRelay) { rdpurl += '&local=1'; }
3113 safeNewWindow(rdpurl, 'mcmstsc/' + message.nodeid);
3114 } else if (message.tag == 'ssh') {
3115 var sshurl = window.location.origin + domainUrl + 'ssh.html?ws=' + message.cookie + (urlargs.key?('&key=' + urlargs.key):'');
3116 var node = getNodeFromId(message.nodeid);
3117 if (node != null) { sshurl += '&name=' + encodeURIComponentEx(node.name); }
3118 if (message.localRelay) { sshurl += '&local=1'; }
3119 safeNewWindow(sshurl, 'mcssh/' + message.nodeid);
3120 }
3121 break;
3122 }
3123 case 'getNotes': {
3124 var n = Q('d2devNotes');
3125 if (n && (message.id == decodeURIComponent(n.attributes['noteid'].value))) {
3126 if (message.notes) { QH('d2devNotes', decodeURIComponent(message.notes)); } else { QH('d2devNotes', ''); }
3127 var ro = (n.attributes['ro'].value == 'true');
3128 if (ro == false) { // If we have permissions, set read/write on this note.
3129 n.removeAttribute('readonly');
3130 QE('idx_dlgOkButton', true);
3131 QV('idx_dlgOkButton', true);
3132 focusTextBox('d2devNotes');
3133 }
3134 }else{
3135 Q('notesPanelArea').innerHTML = (message.notes && marked && DOMPurify) ? DOMPurify.sanitize(marked.parse(decodeURIComponent(message.notes), { breaks: true }), { USE_PROFILES: { html: true } }) : '';
3136 if ((showNotesPanel === 'true') && message.notes) { QV('notesPanel',true); }else{ QV('notesPanel', false); }
3137 }
3138 break;
3139 }
3140 case 'otpauth-request': {
3141 if ((xxdialogMode == 2) && (xxdialogTag == 'otpauth-request')) {
3142 if (message.err != null) {
3143 var otpauthErrors = [ '', "2FA is locked", "Backup codes are locked", "Login token in use", "OTP 2FA not allowed", "Account is locked", "Unable to load OTPLIB" ];
3144 if ((message.err > 0) && (message.err < otpauthErrors.length)) { QH('d2optinfo', otpauthErrors[message.err]); } else { QH('d2optinfo', format("Error #{0}", message.err)); }
3145 } else {
3146 var secret = message.secret;
3147 if (secret.length == 52) { secret = secret.split(/(.............)/).filter(Boolean).join(' '); }
3148 else if (secret.length == 32) { secret = secret.split(/(....)/).filter(Boolean).join(' '); secret = secret.substring(0, 20) + '<br/>' + secret.substring(20) }
3149 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 <img src=images/link4.png height=10 width=10 title="' + "Copy Secret to clipboard" + '" style=cursor:pointer onclick=d2CopySecretToClip()>' + '<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>');
3150 new QRCode(Q('qrcode'), { text: message.url, width: 128, height: 128, colorDark: '#000000', colorLight: '#EEE', correctLevel: QRCode.CorrectLevel.H });
3151 QV('idx_dlgOkButton', true);
3152 QE('idx_dlgOkButton', false);
3153 Q('d2otpauthinput').focus();
3154 }
3155 }
3156 break;
3157 }
3158 case 'otpauth-setup': {
3159 if (xxdialogMode) return;
3160 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."));
3161 break;
3162 }
3163 case 'otpauth-clear': {
3164 if (xxdialogMode) return;
3165 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."));
3166 break;
3167 }
3168 case 'otpauth-getpasswords': {
3169 if (xxdialogMode) return;
3170 var x = "One time tokens can be used as secondary authentication. Generate a set, print them and keep them in a safe place.";
3171 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>';
3172 if (message.passwords) {
3173 var j = 0, clipb = '';
3174 for (var i in message.passwords) {
3175 if (++j % 2) { x += '<tr>'; }
3176 var p = '' + message.passwords[i].p;
3177 while (p.length < 8) { p = '0' + p; }
3178 if (message.passwords[i].u === true) {
3179 x += '<td>' + p.substring(0, 4) + '&nbsp;' + p.substring(4);
3180 if (clipb != '') { clipb += ' '; }
3181 clipb += p;
3182 } else {
3183 x += '<td><strike style=color:#BBB>' + p.substring(0, 4) + '&nbsp;' + p.substring(4) + '</strike>';
3184 }
3185 }
3186 } else {
3187 x += '<tr><td>' + "No Active Tokens";
3188 }
3189 x += '</table></div></div><br />';
3190 x += '<div><input type=button value=' + "Close" + ' onclick=setDialogMode(0) style=float:right></input>';
3191 x += '<input type=button value="' + "Generate New Tokens" + '" onclick="account_manageOtp(1);"></input>';
3192 if (message.passwords != null) {
3193 x += '<input type=button value="' + "Clear Tokens" + '" onclick="account_manageOtp(2);"></input>';
3194 x += '&nbsp;<img src=images/link4.png height=10 width=10 title="' + "Copy valid codes to clipboard" + '" style=cursor:pointer onclick=copyTextToClip2("' + encodeURIComponentEx(clipb) + '")>';
3195 }
3196 x += '</div><br />';
3197 setDialogMode(2, "Manage Backup Codes", 8, null, x, 'otpauth-manage');
3198 break;
3199 }
3200 case 'otp-hkey-get': {
3201 if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) return;
3202 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>';
3203 var end = '</table></div></div>';
3204 var x = '<a href="https://www.yubico.com/" rel="noreferrer noopener" target="_blank">' + "Hardware keys" + '</a> ' + "are used as secondary login authentication.";
3205 x += '<div style="max-height:150px;overflow-y:auto;overflow-x:hidden;margin-top:6px;margin-bottom:6px">';
3206 if (message.keys && message.keys.length > 0) {
3207 for (var i in message.keys) {
3208 var key = message.keys[i], type = (key.type == 2)?'OTP':'WebAuthn';
3209 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;
3210 }
3211 } else {
3212 x += start + '<tr style=text-align:center><td>' + "No Keys Configured" + end;
3213 }
3214 x += '</div>';
3215 x += '<div><input type=button value="' + "Close" + '" onclick=setDialogMode(0) style=float:right></input>';
3216 var hkeycount = (typeof userinfo.otphkeys == 'number') ? userinfo.otphkeys : 0;
3217 if ((typeof serverinfo.maxfidokeys != 'number') || (serverinfo.maxfidokeys > hkeycount)) { // Check if we we reached maximum hardware keys
3218 if ((features & 0x00020000) != 0) { x += '<input id=d2addkey3 type=button value="' + "Add Key" + '" onclick="account_addhkey(3);"></input>'; }
3219 if ((features & 0x00004000) != 0) { x += '<input id=d2addkey2 type=button value="' + "Add YubiKey&reg; OTP" + '" onclick="account_addhkey(2);"></input>'; }
3220 } else {
3221 x += "Maximum keys reached.";
3222 }
3223 x += '</div><br />';
3224 setDialogMode(2, "Manage Security Keys", 8, null, x, 'otpauth-hardware-manage');
3225 if (u2fSupported() == false) { QE('d2addkey1', false); }
3226 break;
3227 }
3228 case 'otp-hkey-yubikey-add': {
3229 if (message.result) {
3230 meshserver.send({ action: 'otp-hkey-get' }); // Success, ask for the full list of keys.
3231 } else {
3232 setDialogMode(2, "Add Security Key", 1, null, '<br />' + "Error, Unable to add key." + '<br /><br />');
3233 }
3234 break;
3235 }
3236 case 'otp-hkey-setup-response': {
3237 if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) return;
3238 if (message.result == true) {
3239 meshserver.send({ action: 'otp-hkey-get' }); // Success, ask for the full list of keys.
3240 } else {
3241 setDialogMode(2, "Add Security Key", 1, null, '<br />' + "ERROR: Unable to add key." + '<br /><br />', 'otpauth-hardware-manage');
3242 }
3243 break;
3244 }
3245 case 'webauthn-startregister': {
3246 if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) return;
3247 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 + ' />';
3248 setDialogMode(2, "Add Security Key", 2, null, x);
3249
3250 var publicKey = message.request;
3251 message.request.challenge = Uint8Array.from(atob(message.request.challenge), function (c) { return c.charCodeAt(0) })
3252 message.request.user.id = Uint8Array.from(atob(message.request.user.id), function (c) { return c.charCodeAt(0) })
3253 navigator.credentials.create({ publicKey: publicKey }).then(function(newCredentialInfo) {
3254 // Public key credential
3255 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 } });
3256 setDialogMode(0);
3257 }, function(error) {
3258 // Error
3259 console.log('ERROR: ' + error);
3260 setDialogMode(2, "Add Security Key", 1, null, "ERROR: " + error);
3261 });
3262 break;
3263 }
3264 case 'verifyPhone': {
3265 if (xxdialogMode && (xxdialogTag != 'verifyPhone')) return;
3266 var x = '<table><tr><td><img src="images/phone80.png" style=padding:8px>';
3267 x += '<td>' + "Check your phone and enter the verification code.";
3268 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>';
3269 setDialogMode(2, "Phone Notifications", 3, account_managePhoneConfirm, x, message.cookie);
3270 Q('d2phoneCodeInput').focus();
3271 account_managePhoneCodeValidate();
3272 break;
3273 }
3274 case 'verifyMessaging': {
3275 if (xxdialogMode && (xxdialogTag != 'verifyMessaging')) return;
3276 var x = '<table><tr><td><img src="images/messaging40.png" style=padding:8px>';
3277 x += '<td>' + "Check your messaging application and enter the verification code.";
3278 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>';
3279 setDialogMode(2, "Messaging Notifications", 3, account_manageMessagingConfirm, x, message.cookie);
3280 Q('d2phoneCodeInput').focus();
3281 account_managePhoneCodeValidate();
3282 break;
3283 }
3284 case 'fileoperation': {
3285 // View the file in the dialog box
3286 var p5editSaveBack = function(b, tag) {
3287 var data;
3288 var value = Q('d4editorarea').value;
3289 value = d4EditLineBreakVal === 0 ? value.replace(/\r?\n|\r/g, '\r\n') : // Windows
3290 d4EditLineBreakVal === 2 ? value.replace(/\r\n|\n/g, '\r') : // Mac
3291 value.replace(/\r\n|\r/g, '\n'); // Linux
3292 if (d4EditEncodingVal == 1) {
3293 data = encode_utf8(value); // UTF8 encoding
3294 } else {
3295 data = value; // RAW encoding
3296 }
3297 meshserver.send({ action: 'fileoperation', fileop: 'set', path: tag.path, file: tag.file, data: btoa(data) });
3298 }
3299 setDialogMode(4, EscapeHtml(message.file), 3, p5editSaveBack, null, message);
3300 QV('d4EncodingButton', true);
3301 QV('d4LineBreakButton', true);
3302 QS('dialog').width = 'auto';
3303 QS('dialog').bottom = '80px';
3304 QS('dialog').top = QS('dialog').left = QS('dialog').right = '100px';
3305 if (d4EditEncodingVal == 1) {
3306 Q('d4editorarea').value = decode_utf8(atob(message.data)); // UTF8 Encoding
3307 } else {
3308 Q('d4editorarea').value = atob(message.data); // RAW Encoding
3309 }
3310 break;
3311 }
3312 case 'event': {
3313 if (!message.event.nolog) {
3314 if (currentNode && (message.event.nodeid == currentNode._id) && (currentDeviceEvents != null)) {
3315 // If this event has a nodeid and we are looking at this node, update the log in real time.
3316 if ((message.event.action == p16filterevents.value) || (p16filterevents.value == "")) {
3317 if(currentDeviceEvents != null) {
3318 currentDeviceEvents.unshift(message.event);
3319 var eventLimit = parseInt(p16limitdropdown.value);
3320 while (currentDeviceEvents.length > eventLimit) { currentDeviceEvents.pop(); } // Remove element(s) at the end
3321 }
3322 mainUpdate(1024);
3323 }
3324 }
3325
3326 if (currentUser && (message.event.userid == currentUser._id)) {
3327 // If this event has a userid and we are looking at this user, update the log in real time.
3328 if ((message.event.action == p31filterevents.value) || (p31filterevents.value == "")) {
3329 if(currentUserEvents != null) {
3330 currentUserEvents.unshift(message.event);
3331 var eventLimit = parseInt(p31limitdropdown.value);
3332 while (currentUserEvents.length > eventLimit) { currentUserEvents.pop(); } // Remove element(s) at the end
3333 }
3334 mainUpdate(2048);
3335 }
3336 }
3337
3338 // Add this event to the main events log.
3339 if ((message.event.action == p3filterevents.value) || (p3filterevents.value == "")) {
3340 if(events != null) {
3341 events.unshift(message.event);
3342 var eventLimit = parseInt(p3limitdropdown.value);
3343 while (events.length > eventLimit) { events.pop(); } // Remove element(s) at the end
3344 }
3345 mainUpdate(32);
3346 }
3347 }
3348 if (message.event.noact) break; // Take no action on this event
3349
3350 switch (message.event.action) {
3351 case 'serverinfochange': {
3352 if (message.event.lock2factor != null) { serverinfo.lock2factor = message.event.lock2factor; updateSelf(); updateSiteAdmin(); }
3353 break;
3354 }
3355 case 'deviceShareUpdate': {
3356 if (message.event.nodeid != deviceSharesReq) break;
3357 deviceSharesNode = message.event.nodeid;
3358 deviceShares = message.event.deviceShares;
3359 if (currentNode._id == deviceSharesNode) { gotoDevice(currentNode._id, xxcurrentView, true); }
3360 break;
3361 }
3362 case 'agentlog': {
3363 if (message.event.msgid == 98) {
3364 // This is a agent help request, popup a notification.
3365 if (GetNodeRights(message.event.nodeid) > 0) {
3366 var n = getNodeFromId(message.event.nodeid);
3367 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 });
3368 }
3369 }
3370 break;
3371 }
3372 case 'recording': {
3373 if ((p52recordings != null) && (typeof p52recordings == 'object')) { p52recordings.unshift(message.event); message.event.present = 1; updateRecordings(); }
3374 break;
3375 }
3376 case 'userWebState': {
3377 // New user web state, update the web page as needed
3378 try {
3379 if (localStorage != null) {
3380 // TODO: The problem with this "old" values is that if changed from a different tab, the old and new values will be the same.
3381 // So comparing against these values is not a good idea.
3382 var oldShowRealNames = localStorage.getItem('showRealNames');
3383 var oldUiMode = localStorage.getItem('uiMode');
3384 var oldSort = localStorage.getItem('sort');
3385 var oldLoctag = localStorage.getItem('loctag');
3386 var oldNightMode = localStorage.getItem('nightMode');
3387 var oldFooterBar = localStorage.getItem('footerBar');
3388
3389 var webstate = JSON.parse(message.event.state);
3390 for (var i in webstate) { localStorage.setItem(i, webstate[i]); }
3391
3392 // Update the web page
3393 //if ((webstate.deskAspectRatio != null) && (webstate.deskAspectRatio != deskAspectRatio)) { deskAspectRatio = webstate.deskAspectRatio; deskAdjust(); }
3394 if ((webstate.showRealNames != null) && (webstate.showRealNames != oldShowRealNames)) { showRealNames = Q('RealNameCheckBox').checked = (webstate.showRealNames == '1'); mainUpdate(6); }
3395 if ((webstate.uiMode != null) && (webstate.uiMode != oldUiMode)) { userInterfaceSelectMenu(parseInt(webstate.uiMode)); }
3396 if ((webstate.sort != null) && (webstate.sort != oldSort)) { document.getElementById('sortselect').selectedIndex = sort = parseInt(webstate.sort); mainUpdate(6); }
3397 if ((webstate.loctag != null) && (webstate.loctag != oldLoctag)) { if (webstate.loctag != null) { args.locale = webstate.loctag; } else { delete args.locale; } mainUpdate(0xFFFFFFFF); }
3398 if ((webstate.nightMode != null) && (webstate.nightMode != oldNightMode)) { putstore('nightMode', webstate.nightMode); setNightMode(); }
3399 if ((webstate.footerBar != null) && (webstate.footerBar != oldFooterBar)) { footerBar = (webstate.footerBar == '1'); QS('container')['grid-template-rows'] = null; QS('container')['-ms-grid-rows'] = null; adjustPanels(); }
3400 if ((webstate.stars != null) && (webstate.stars != JSON.stringify(stars))) {
3401 stars = JSON.parse(webstate.stars);
3402 if (Q('DevFilterSelect').value == 3) { mainUpdate(5); } else { mainUpdate(4); }
3403 if (currentNode) { refreshDevice(currentNode._id); }
3404 }
3405 if (webstate.deskKeyShortcuts != null) {
3406 // Set the user's desktop shortcut keys
3407 deskKeyboardShortcuts = [];
3408 var deskKeyboardShortcutsStr = webstate.deskKeyShortcuts.split(',');
3409 for (var i in deskKeyboardShortcutsStr) { if (deskKeyboardShortcutsStr[i] != "") { deskKeyboardShortcuts.push(parseInt(deskKeyboardShortcutsStr[i])); } }
3410 updateDeskShortcutKeys();
3411 }
3412 if (webstate.deskStrings != null) {
3413 // Set the user's desktop strings
3414 var s = null;
3415 try { s = JSON.parse(webstate.deskStrings); } catch (ex) {}
3416 if (Array.isArray(s)) { deskKeyboardStrings = s; updateDesktopStrings(); }
3417 }
3418 }
3419 } catch (ex) {}
3420 break;
3421 }
3422 case 'servertimelinestats': { addServerTimelineStats(message.event.data); break; }
3423 case 'accountcreate':
3424 case 'accountchange': {
3425 // An account was created or changed
3426 if ((typeof message.event.account != 'object') || (message.event.account == null)) { console.log(message.event); return; }
3427 if (userinfo._id == message.event.account._id) {
3428 var newsiteadmin = message.event.account.siteadmin?message.event.account.siteadmin:0;
3429 var oldsiteadmin = userinfo.siteadmin?userinfo.siteadmin:0;
3430 var newRemoveRights = message.event.account.removeRights?message.event.account.removeRights:0;
3431 var oldRemoveRights = userinfo.removeRights?userinfo.removeRights:0;
3432 if ((message.event.account.quota != userinfo.quota) || (((userinfo.siteadmin & 8) == 0) && ((message.event.account.siteadmin & 8) != 0))) { meshserver.send({ action: 'files' }); }
3433 var oldgroups = userinfo.groups;
3434 userinfo = message.event.account;
3435 if ((oldsiteadmin != newsiteadmin) || (oldRemoveRights != newRemoveRights) || (message.event.accountImageChange == 1)) { // If the site admin permission or user image has changed...
3436 if (message.event.accountImageChange == 1) { userinfo.accountImageRnd = Math.floor(Math.random() * 9999999999); }
3437 updateSiteAdmin();
3438 }
3439 updateSelf();
3440
3441 if ((userinfo.siteadmin & 2) != 0) {
3442 // Compare our groups
3443 var og = oldgroups ? oldgroups : [];
3444 var ng = userinfo.groups ? userinfo.groups : [];
3445 if (og.join(',') != ng.join(',')) {
3446 // Our groups have changed, re-ask for a list of users.
3447 users = wssessions = null;
3448 meshserver.send({ action: 'users' });
3449 meshserver.send({ action: 'wssessioncount' });
3450 }
3451 }
3452
3453 // If our list of nodes may have changes, request the new list now.
3454 if (message.event.nodeListChange == userinfo._id) { meshserver.send({ action: 'nodes', skip: (devicePagingState == null) ? 0 : devicePagingState.skip }); }
3455 }
3456 if (currentNode) { refreshDevice(currentNode._id); }
3457 if (users == null) break;
3458
3459 // Check if the account is part of our user group
3460 if ((userinfo.groups == null) || (userinfo.groups.length == 0) || (findOne(message.event.account.groups, userinfo.groups) == true)) {
3461 users[message.event.account._id] = message.event.account; // Part of our groups, update this user.
3462 } else {
3463 delete users[message.event.account._id]; // No longer part of our groups, remove this user.
3464 }
3465
3466 mainUpdate(4 | 16384);
3467 break;
3468 }
3469 case 'accountremove': {
3470 // An account was removed
3471 if (users == null) break;
3472 delete users[message.event.userid];
3473 mainUpdate(16384);
3474 break;
3475 }
3476 case 'createusergroup':
3477 case 'usergroupchange': {
3478 // User group changed
3479 if (usergroups == null) { usergroups = {}; }
3480 var ugroup = usergroups[message.event.ugrpid];
3481 if (ugroup == null) {
3482 // This is a new user group for us
3483 usergroups[message.event.ugrpid] = { _id: message.event.ugrpid, name: message.event.name, desc: message.event.desc, domain: message.event.domain, links: message.event.links };
3484 } else {
3485 // This is an existing user group
3486 ugroup.name = message.event.name;
3487 if (message.event.desc) { ugroup.desc = message.event.desc; } else { delete ugroup.desc; }
3488 if (message.event.links) { ugroup.links = message.event.links; } else { delete ugroup.links; }
3489 if (message.event.flags) { ugroup.flags = message.event.flags; } else { delete ugroup.flags; }
3490 if (typeof message.event.consent == 'number') { ugroup.consent = message.event.consent; }
3491 }
3492 mainUpdate(4096 + 8192 + 16384);
3493
3494 // Group update, refresh all our device groups and nodes. TODO: Optimize this to only do this when needed.
3495 meshserver.send({ action: 'meshes' });
3496 meshserver.send({ action: 'nodes', skip: (devicePagingState == null) ? 0 : devicePagingState.skip });
3497 break;
3498 }
3499 case 'deleteusergroup': {
3500 // User group removed
3501 if ((usergroups != null) && (usergroups[message.event.ugrpid] != null)) {
3502 delete usergroups[message.event.ugrpid];
3503 var c = 0;
3504 for (var i in usergroups) { c++; }
3505 if (c == 0) { usergroups = null; } // If user groups is empty, set it to null.
3506 mainUpdate(8192 + 16384);
3507 }
3508 break;
3509 }
3510 case 'createmesh': {
3511 // A new mesh was created
3512 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.
3513 meshes[message.event.meshid] = message.event.mesh;
3514 mainUpdate(4 + 128 + 8192 + 16384);
3515 meshserver.send({ action: 'files' });
3516 }
3517 break;
3518 }
3519 case 'meshchange': {
3520 // Update mesh information
3521 if (meshes[message.event.meshid] == null) {
3522 // Check if we have any access to this device group
3523 var add = false;
3524 if (message.event.links[userinfo._id] != null) { add = true; }
3525 if (userinfo.links[message.event.meshid] != null) { add = true; }
3526 for (var i in userinfo.links) { if ((i.startsWith('ugrp/')) && (message.event.links[i] != null)) { add = true; } }
3527
3528 // This is a new mesh for us
3529 if (add) {
3530 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 };
3531 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).
3532 }
3533 } else {
3534 // This is an existing device group
3535 if (message.event.name != null) {
3536 meshes[message.event.meshid].name = message.event.name;
3537 for (var i in nodes) { if (nodes[i].meshid == message.event.meshid) { nodes[i].meshnamel = message.event.name.toLowerCase(); } }
3538 }
3539 if (message.event.desc != null) { meshes[message.event.meshid].desc = message.event.desc; }
3540 if (message.event.flags != null) { meshes[message.event.meshid].flags = message.event.flags; }
3541 if (message.event.consent != null) { meshes[message.event.meshid].consent = message.event.consent; }
3542 if (message.event.links) { meshes[message.event.meshid].links = message.event.links; }
3543 if (message.event.amt) { meshes[message.event.meshid].amt = message.event.amt; }
3544 if (message.event.invite != null) { meshes[message.event.meshid].invite = message.event.invite; } else { delete meshes[message.event.meshid].invite; }
3545 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; } }
3546 if (message.event.relayid != null) { meshes[message.event.meshid].relayid = message.event.relayid; }
3547
3548 // Check if we lost rights to this mesh in this change.
3549 if (IsMeshViewable(message.event.meshid) == false) {
3550 if ((xxcurrentView == 20) && (currentMesh == meshes[message.event.meshid])) go(2);
3551 delete meshes[message.event.meshid];
3552
3553 // Delete all nodes in that mesh, except ones with direct links
3554 var newnodes = [];
3555 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]); } }
3556 nodes = newnodes;
3557
3558 // If we are looking at a node that is no longer visible, move back to "My Devices"
3559 if ((xxcurrentView >= 10) && (xxcurrentView < 20) && currentNode && !IsNodeViewable(currentNode)) { setDialogMode(0); go(1); }
3560 }
3561 }
3562 mainUpdate(4 + 128 + 8192 + 16384);
3563 if (currentNode && !IsNodeViewable(currentNode)) { currentNode = null; if ((xxcurrentView >= 10) && (xxcurrentView < 20)) { go(1); } }
3564 //meshserver.send({ action: 'files' }); // TODO: Why do we need to do this??
3565
3566 // If we are looking at a mesh that is now deleted, move back to "My Account"
3567 if (xxcurrentView == 20 && currentMesh._id == message.event.meshid) { mainUpdate(4096); }
3568 break;
3569 }
3570 case 'deletemesh': {
3571 // Delete the mesh
3572 if (meshes[message.event.meshid]) {
3573 delete meshes[message.event.meshid];
3574 mainUpdate(128);
3575 meshserver.send({ action: 'files' });
3576 }
3577
3578 // Delete all nodes in that mesh
3579 var newnodes = [];
3580 if (nodes != null) { for (var i in nodes) { if (nodes[i].meshid != message.event.meshid) { newnodes.push(nodes[i]); } } }
3581 nodes = newnodes;
3582 mainUpdate(4 + 8192 + 16384);
3583
3584 // If we are looking at a mesh that is now deleted, move back to "My Account"
3585 if (xxcurrentView >= 20 && xxcurrentView < 30 && currentMesh._id == message.event.meshid) { setDialogMode(0); go(2); }
3586 // If we are looking at a node in the deleted mesh, move back to "My Devices"
3587 if (xxcurrentView >= 10 && xxcurrentView < 20 && currentNode && !IsNodeViewable(currentNode)) { setDialogMode(0); go(1); }
3588 break;
3589 }
3590 case 'addnode': {
3591 var node = message.event.node;
3592 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.
3593 if (getNodeFromId(node._id) != null) break; // This node is already known.
3594 node.namel = node.name.toLowerCase();
3595 if (node.rname) { node.rnamel = node.rname.toLowerCase(); } else { node.rnamel = node.namel; }
3596 node.meshnamel = meshes[node.meshid]?meshes[node.meshid].name.toLowerCase():'*';
3597 node.state = 0;
3598 if (!node.icon) node.icon = 1;
3599 node.ident = ++nodeShortIdent;
3600 if (nodes == null) { }
3601 nodes.push(node);
3602
3603 // Web page update
3604 mainUpdate(1 | 2 | 4 | 16);
3605
3606 // If we are looking at the device group for this device, update that.
3607 if ((xxcurrentView == 20) && (currentMesh != null) && (currentMesh._id == node.meshid)) { mainUpdate(4096); }
3608
3609 break;
3610 }
3611 case 'removenode': {
3612 var index = -1;
3613 for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } }
3614 if (index != -1) {
3615 var node = nodes[index];
3616 if (currentNode == node) {
3617 if (xxcurrentView >= 10 && xxcurrentView < 20) { setDialogMode(0); go(1); }
3618 currentNode = null;
3619 // TODO: Correctly disconnect from this node (Desktop/Terminal/Files...)
3620 }
3621 nodes.splice(index, 1);
3622
3623 // Web page update
3624 mainUpdate(4 | 16);
3625
3626 // If we are looking at the device group for this device, update that.
3627 if ((xxcurrentView == 20) && (currentMesh != null) && (currentMesh._id == node.meshid)) { mainUpdate(4096); }
3628 }
3629 break;
3630 }
3631 case 'changenode': {
3632 var index = -1;
3633 for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } }
3634 if (index != -1) {
3635 var node = nodes[index];
3636
3637 // Change the node
3638 node.name = message.event.node.name;
3639 node.rname = message.event.node.rname;
3640 node.lusers = message.event.node.lusers;
3641 node.upnusers = message.event.node.upnusers;
3642 node.users = message.event.node.users;
3643 node.host = message.event.node.host;
3644 node.desc = message.event.node.desc;
3645 node.ip = message.event.node.ip;
3646 node.osdesc = message.event.node.osdesc;
3647 node.publicip = message.event.node.publicip;
3648 node.iploc = message.event.node.iploc;
3649 node.wifiloc = message.event.node.wifiloc;
3650 node.gpsloc = message.event.node.gpsloc;
3651 node.tags = message.event.node.tags;
3652 node.ssh = message.event.node.ssh;
3653 node.rdp = message.event.node.rdp;
3654 node.userloc = message.event.node.userloc;
3655 node.rdpport = message.event.node.rdpport;
3656 node.rfbport = message.event.node.rfbport;
3657 node.sshport = message.event.node.sshport;
3658 node.httpport = message.event.node.httpport;
3659 node.httpsport = message.event.node.httpsport;
3660 node.consent = message.event.node.consent;
3661 node.pmt = message.event.node.pmt;
3662 if (message.event.node.links != null) { node.links = message.event.node.links; } else { delete node.links; }
3663 if (message.event.node.agent != null) {
3664 if (node.agent == null) node.agent = {};
3665 if (message.event.node.agent.ver != null) { node.agent.ver = message.event.node.agent.ver; }
3666 if (message.event.node.agent.id != null) { node.agent.id = message.event.node.agent.id; }
3667 if (message.event.node.agent.caps != null) { node.agent.caps = message.event.node.agent.caps; }
3668 if (message.event.node.agent.root != null) { node.agent.root = message.event.node.agent.root; }
3669 if (message.event.node.agent.core != null) { node.agent.core = message.event.node.agent.core; } else { if (node.agent.core) { delete node.agent.core; } }
3670 node.agent.tag = message.event.node.agent.tag;
3671 }
3672 if (message.event.node.intelamt != null) {
3673 if (node.intelamt == null) node.intelamt = {};
3674 if (message.event.node.intelamt.state != null) { node.intelamt.state = message.event.node.intelamt.state; }
3675 if (message.event.node.intelamt.host != null) { node.intelamt.user = message.event.node.intelamt.host; }
3676 if (message.event.node.intelamt.user != null) { node.intelamt.user = message.event.node.intelamt.user; } else { delete node.intelamt.user; }
3677 if (message.event.node.intelamt.tls != null) { node.intelamt.tls = message.event.node.intelamt.tls; }
3678 if (message.event.node.intelamt.ver != null) { node.intelamt.ver = message.event.node.intelamt.ver; }
3679 if (message.event.node.intelamt.tag != null) { node.intelamt.tag = message.event.node.intelamt.tag; }
3680 if (message.event.node.intelamt.uuid != null) { node.intelamt.uuid = message.event.node.intelamt.uuid; }
3681 if (message.event.node.intelamt.realm != null) { node.intelamt.realm = message.event.node.intelamt.realm; }
3682 if (message.event.node.intelamt.flags != null) { node.intelamt.flags = message.event.node.intelamt.flags; }
3683 if (message.event.node.intelamt.warn != null) { node.intelamt.warn = message.event.node.intelamt.warn; } else { delete node.intelamt.warn; }
3684 }
3685 if (message.event.node.av != null) { node.av = message.event.node.av; }
3686 if (message.event.node.lsc != null) { node.lsc = message.event.node.lsc; }
3687 if (message.event.node.wsc != null) { node.wsc = message.event.node.wsc; }
3688 if (message.event.node.defender != null) { node.defender = message.event.node.defender; }
3689 if (message.event.node.volumes != null) { node.volumes = message.event.node.volumes; }
3690 node.namel = node.name.toLowerCase();
3691 if (node.rname) { node.rnamel = node.rname.toLowerCase(); } else { node.rnamel = node.namel; }
3692 if (message.event.node.icon) { node.icon = message.event.node.icon; }
3693 if (message.event.node.lastbootuptime != null) { node.lastbootuptime = message.event.node.lastbootuptime; }
3694 if (message.event.node.idletime != null) { node.idletime = message.event.node.idletime; }
3695
3696 // Check if this device has changed device group
3697 if (message.event.node.meshid != node.meshid) {
3698 if ((meshes[message.event.node.meshid] == null) && ((userinfo.links == null) || (userinfo.links[node._id] == null))) {
3699 // We don't see the new mesh, remove this device
3700 // TODO: Correctly disconnect from this node (Desktop/Terminal/Files...)
3701 if ((currentNode != null) && (currentNode._id == node._id)) {
3702 if ((xxcurrentView >= 10) && (xxcurrentView < 20) && !IsNodeViewable(currentNode)) { currentNode = null; setDialogMode(0); go(1); }
3703 }
3704 var index = -1;
3705 for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } }
3706 nodes.splice(index, 1);
3707
3708 mainUpdate(4 | 16);
3709 } else {
3710 // We see the new mesh, move this device
3711 node.meshid = message.event.node.meshid;
3712 node.meshnamel = meshes[message.event.node.meshid]?meshes[message.event.node.meshid].name.toLowerCase():'*';
3713 mainUpdate(1 | 2 | 4);
3714 }
3715 }
3716
3717 // Web page update
3718 updateDeviceViewDevice(node);
3719 mainUpdate(2 | 8 | 16);
3720 refreshDevice(node._id);
3721
3722 // If we are looking at the device group for this device, update that.
3723 if ((xxcurrentView == 20) && (currentMesh != null) && (currentMesh._id == node.meshid)) { mainUpdate(4096); }
3724
3725 if ((currentNode == node) && (xxdialogMode != null) && (xxdialogTag == '@xxmap')) { p10showNodeLocationDialog(); }
3726 }
3727 break;
3728 }
3729 case 'nodemeshchange': {
3730 var index = -1;
3731 for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } }
3732 if (index != -1) {
3733 var node = nodes[index];
3734 if ((meshes[message.event.newMeshId] == null) && ((userinfo.links == null) || (userinfo.links[node._id] == null))) {
3735 // We don't see the new mesh, remove this device
3736
3737 // TODO: Correctly disconnect from this node (Desktop/Terminal/Files...)
3738 if ((currentNode != null) && (currentNode._id == node._id)) {
3739 if ((xxcurrentView >= 10) && (xxcurrentView < 20) && !IsNodeViewable(currentNode)) { currentNode = null; setDialogMode(0); go(1); }
3740 }
3741 nodes.splice(index, 1);
3742 mainUpdate(4 | 16);
3743 } else {
3744 // We see the new mesh, move this device
3745 node.meshid = message.event.newMeshId;
3746 node.meshnamel = meshes[message.event.newMeshId]?meshes[message.event.newMeshId].name.toLowerCase():'*';
3747 mainUpdate(1 | 2 | 4);
3748 }
3749 refreshDevice(message.event.nodeid);
3750 } else {
3751 // This is a new device, add it.
3752 var node = message.event.node;
3753 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.
3754 node.namel = node.name.toLowerCase();
3755 if (node.rname) { node.rnamel = node.rname.toLowerCase(); } else { node.rnamel = node.namel; }
3756 node.meshnamel = meshes[node.meshid]?meshes[node.meshid].name.toLowerCase():'*';
3757 node.state = 0;
3758 if (!node.icon) node.icon = 1;
3759 node.ident = ++nodeShortIdent;
3760 if (nodes == null) { }
3761 nodes.push(node);
3762
3763 // Web page update
3764 mainUpdate(1 | 2 | 4 | 16);
3765 }
3766 break;
3767 }
3768 case 'nodeconnect': {
3769 // Indicated a node has changed connectivity state
3770 var index = -1;
3771 for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } }
3772 if (index != -1) {
3773 var node = nodes[index];
3774
3775 // Event the connection change if needed
3776 var n = getstore('notifications', 0); // Account notification settings
3777
3778 // Per-group notification settings
3779 if (message.event.meshid && userinfo.links && userinfo.links[message.event.meshid] && userinfo.links[message.event.meshid].notify) {
3780 n |= userinfo.links[message.event.meshid].notify;
3781 }
3782
3783 // Per-user notification settings
3784 if (userinfo.notify != null) {
3785 if (userinfo.notify[message.event.meshid] != null) { n |= userinfo.notify[message.event.meshid]; }
3786 if (userinfo.notify[message.event.nodeid] != null) { n |= userinfo.notify[message.event.nodeid]; }
3787 }
3788
3789 // Show the notification
3790 if (n & 2) {
3791 var agentPrivilages = "Agent connected";
3792 if ((node.agent != null) && (node.agent.root === false)) { agentPrivilages = "Agent connected with limited privilages"; }
3793 var abc = { title: node.name, icon: node.icon, nodeid: node._id, id: message.event.id };
3794 if (((node.conn & 1) == 0) && ((message.event.conn & 1) != 0)) { abc.text = agentPrivilages; addNotification(abc); }
3795 if (((node.conn & 2) == 0) && ((message.event.conn & 2) != 0)) { abc.text = "Intel AMT CIRA connected"; addNotification(abc); }
3796 if (((node.conn & 4) == 0) && ((message.event.conn & 4) != 0)) { abc.text = "Intel AMT connected"; addNotification(abc); }
3797 if (((node.conn & 16) == 0) && ((message.event.conn & 16) != 0)) { abc.text = "MQTT connected"; addNotification(abc); }
3798 }
3799 if (n & 4) {
3800 var abc = { title: node.name, icon: node.icon, nodeid: node._id, id: message.event.id };
3801 if (((node.conn & 1) != 0) && ((message.event.conn & 1) == 0)) { abc.text = "Agent disconnected"; addNotification(abc); }
3802 if (((node.conn & 2) != 0) && ((message.event.conn & 2) == 0)) { abc.text = "Intel AMT CIRA disconnected"; addNotification(abc); }
3803 if (((node.conn & 4) != 0) && ((message.event.conn & 4) == 0)) { abc.text = "Intel AMT disconnected"; addNotification(abc); }
3804 if (((node.conn & 16) != 0) && ((message.event.conn & 16) == 0)) { abc.text = "MQTT disconnected"; addNotification(abc); }
3805 }
3806
3807 // Change the node connection state
3808 node.conn = message.event.conn;
3809 node.pwr = message.event.pwr;
3810 node.lastconnect = Date.now();
3811
3812 // Clear sesssion and battery information if needed
3813 if ((node.conn & 1) == 0) { delete node.sessions; }
3814
3815 // Web page update
3816 var filter = Q('DevFilterSelect').value;
3817 if ((filter == 1) || (filter == 5)) {
3818 // We are looking at online or offline devices. This may change that state.
3819 mainUpdate(1 | 4 | 16);
3820 } else {
3821 // We are looking at a different filter, just update that specific device.
3822 updateDeviceViewDevice(node);
3823 mainUpdate(1 | 16);
3824 }
3825 refreshDevice(node._id);
3826
3827 // If we are looking at the device group for this device, update that.
3828 if ((xxcurrentView == 20) && (currentMesh != null) && (currentMesh._id == node.meshid)) { mainUpdate(4096); }
3829 }
3830 break;
3831 }
3832 case 'wssessioncount': {
3833 // Update the active web socket session count for a user
3834 if (wssessions != null) {
3835 if (message.event.count == 0 && wssessions[message.event.userid]) {
3836 delete wssessions[message.event.userid];
3837 } else {
3838 wssessions[message.event.userid] = message.event.count;
3839 }
3840 mainUpdate(16384);
3841 }
3842 break;
3843 }
3844 case 'login': {
3845 // Update the last login time
3846 if (users != null && users[message.event.userid]) {
3847 users[message.event.userid].login = Math.floor(new Date(message.event.time).getTime() / 1000);
3848 }
3849 break;
3850 }
3851 case 'scanamtdevice': {
3852 // Populate the Intel AMT scan dialog box with the result of the RMCP scan
3853 if ((xxdialogMode == null) || (!Q('dp1range')) || (xxdialogTag != ('AMTSCAN:' + message.event.range))) return;
3854 var x = '';
3855 if (message.event.results == null) {
3856 // The scan could not occur because of an error. Likely the user range was invalid.
3857 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>';
3858 } else {
3859 // Go thru all the results and populate the dialog box
3860 amtScanResults = message.event.results;
3861 for (var i in message.event.results) {
3862 var r = message.event.results[i], shortname = r.hostname;
3863 if (r.hosttype == 'host') { shortname = capitalizeFirstLetter(shortname.split('.')[0]); }
3864 if (shortname.length > 20) { shortname = shortname.substring(0, 20) + '...'; }
3865 var str = '<b title="' + EscapeHtml(r.hostname) + '">' + EscapeHtml(shortname) + '</b> - v' + r.ver;
3866 if (r.state == 2) { if (r.tls == 1) { str += " with TLS."; } else { str += " without TLS."; } } else { str += ' not activated.'; }
3867 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>';
3868 }
3869 // If no results where found, display a nice message
3870 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>'; }
3871 }
3872 // Set the html in the dialog box and re-enable the scan button
3873 QH('dp1results', x);
3874 QE('dp1range', true);
3875 QE('dp1rangebutton', true);
3876 QE('d2scanSelectButton', true);
3877 break;
3878 }
3879 case 'notify': {
3880 var n = { text: message.event.value, title: message.event.title, icon: message.event.icon, titleid: message.titleid, msgid: message.msgid, args: message.args };
3881 if (message.id != null) { n.id = message.id; }
3882 if (message.event.tag != null) { n.tag = message.event.tag; }
3883 if (typeof message.maxtime == 'number') { n.maxtime = message.maxtime; }
3884 addNotification(n);
3885 break;
3886 }
3887 case 'traceinfo': {
3888 if (typeof message.event.traceSources == 'object') {
3889 if ((message.event.traceSources != null) && (message.event.traceSources.length > 0)) {
3890 serverTraceSources = message.event.traceSources;
3891 QH('p41traceStatus', EscapeHtml(message.event.traceSources.join(', ')));
3892 } else {
3893 serverTraceSources = [];
3894 QH('p41traceStatus', "None");
3895 }
3896 }
3897 break;
3898 }
3899 case 'sysinfohash': {
3900 // If the sysinfo document has changed and we are looking at it, request an update.
3901 if ((currentNode != null) && (message.event.nodeid == powerTimelineReq)) {
3902 meshserver.send({ action: 'getsysinfo', nodeid: message.event.nodeid });
3903 }
3904 break;
3905 }
3906 case 'ifchange': {
3907 // Network interface changed for a device, if we are currently viewing this device, ask for an update.
3908 if ((currentNode != null) && (currentNode._id == message.event.nodeid)) { meshserver.send({ action: 'getnetworkinfo', nodeid: currentNode._id }); }
3909 break;
3910 }
3911 case 'devicesessions': {
3912 // List of sessions for a given device
3913 var node = getNodeFromId(message.event.nodeid);
3914 if (node == null) break; // Unknown node
3915 node.sessions = message.event.sessions;
3916 if (node.sessions != null) {
3917 for (var i in node.sessions) { if (Object.keys(node.sessions[i]).length == 0) { delete node.sessions[i]; } }
3918 if (Object.keys(node.sessions).length == 0) { delete node.sessions; }
3919 }
3920 updateDeviceViewDevice(node);
3921 if ((currentNode != null) && (currentNode._id == message.event.nodeid)) { gotoDevice(currentNode._id, xxcurrentView, true); }
3922
3923 // If we are looking at the sessions dialog box for this device now, update it
3924 if (xxdialogTag == ('SESSIONS-' + message.event.nodeid)) { showDeviceSessions(message.event.nodeid, true); }
3925 if (xxdialogTag == ('MESSAGES-' + message.event.nodeid)) { showDeviceMessages(message.event.nodeid, true); }
3926 if (xxdialogTag == ('HELPREQ-' + message.event.nodeid)) { showDeviceHelpRequests(message.event.nodeid, true); }
3927
3928 // If we are filtering on sessions or help, update the visible devices
3929 if ((Q('DevFilterSelect').value == 2) || (Q('DevFilterSelect').value == 6)) { mainUpdate(1); }
3930 break;
3931 }
3932 case 'loginTokenChanged': { // Login tokens have changed
3933 if (message.event.userid != userinfo._id) return;
3934 loginTokens = message.event.loginTokens;
3935 mainUpdate(65536);
3936 break;
3937 }
3938 case 'loginTokenAdded': { // A login token was added
3939 if (message.event.userid != userinfo._id) return;
3940 if (loginTokens == null) { loginTokens = []; }
3941 loginTokens.push(message.event.newToken);
3942 mainUpdate(65536);
3943 break;
3944 }
3945 case 'stopped': { // Server is stopping.
3946 // Disconnect
3947 //console.log(message.msg);
3948 break;
3949 }
3950 case 'updatePluginList': {
3951 installedPluginList = message.event.list;
3952 updatePluginList();
3953 break;
3954 }
3955 case 'pluginStateChange': {
3956 if (pluginHandler == null) break;
3957 pluginHandler.refreshPluginHandler();
3958 break;
3959 }
3960 case 'plugin': {
3961 if (pluginHandler == null) break;
3962 try { pluginHandler[message.event.plugin][message.event.pluginaction](message); } catch (e) { console.log("PluginHandler could not event message: ", e); }
3963 break;
3964 }
3965 default:
3966 //console.log('Unknown message.event.action', message.event.action);
3967 break;
3968 }
3969 break;
3970 }
3971 case 'pluginPermissions': {
3972 handlePluginPermissions(message);
3973 break;
3974 }
3975 case 'pluginPermissionsSet': {
3976 if (message.success) {
3977 // Already handled in save function
3978 } else {
3979 alert("Error saving permissions: " + message.error);
3980 }
3981 break;
3982 }
3983 case 'pluginPermissionList': {
3984 handlePluginPermissionList(message);
3985 break;
3986 }
3987 case 'createInviteLink': { // Agent installation invitation link
3988 if (xxdialogTag != message.meshid) break;
3989 var servername = serverinfo.name;
3990 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.
3991 var domainUrlNoSlash = domainUrl.substring(0, domainUrl.length - 1);
3992 var url;
3993 if (serverinfo.https == true) {
3994 var portStr = (serverinfo.port == 443) ? '' : (':' + serverinfo.port);
3995 url = 'https://' + servername + portStr + domainUrl + 'agentinvite?c=' + message.cookie + (urlargs.key?('&key=' + urlargs.key):'');
3996 } else {
3997 var portStr = (serverinfo.port == 80) ? '' : (':' + serverinfo.port);
3998 url = 'http://' + servername + portStr + domainUrl + 'agentinvite?c=' + message.cookie + (urlargs.key?('&key=' + urlargs.key):'');
3999 }
4000 Q('agentInvitationLink').href = url;
4001 var t = format((message.expire == 1)?"1 hour":"{0} hours", message.expire);
4002 if (message.expire == 24) { t = "1 day"; }
4003 if (message.expire == 168) { t = "1 week"; }
4004 if (message.expire == 5040) { t = "1 month"; }
4005 if (message.expire == 0) { t = "Unlimited"; }
4006 QH('agentInvitationLink', format("Invitation Link ({0})", t));
4007 QV('agentInvitationLinkDiv', true);
4008 break;
4009 }
4010 case 'createDeviceShareLink': { // Guest sharing link
4011 if (xxdialogTag) break;
4012 var node = getNodeFromId(message.nodeid), x = '';
4013 if (node == null) break;
4014 x += addHtmlValue("Device", EscapeHtml(node.name));
4015 x += addHtmlValue("Guest Name", message.guestname);
4016 x += addHtmlValue("User Input", message.viewOnly ? "Not allowed, view only" : "Allowed");
4017 if (message.start && message.expire) {
4018 if (message.recurring) {
4019 x += addHtmlValue("Start Time", printDateTime(new Date(message.start)));
4020 if (message.expire < 2) { x += addHtmlValue("Duration", format("{0} minute", message.expire) ); }
4021 else { x += addHtmlValue("Duration", format("{0} minutes", message.expire) ); }
4022 } else {
4023 x += addHtmlValue("Start Time", printDateTime(new Date(message.start)));
4024 x += addHtmlValue("Expire Time", printDateTime(new Date(message.expire)));
4025 }
4026 }
4027 if (message.recurring == 1) { x += addHtmlValue("Recurring", "Daily"); }
4028 if (message.recurring == 2) { x += addHtmlValue("Recurring", "Weekly"); }
4029 var y = [];
4030 if (message.consent & 0x0007) { y.push("Notify"); }
4031 if (message.consent & 0x0038) { y.push("Prompt"); }
4032 if (message.consent & 0x0040) { y.push("Privacy bar"); }
4033 if (y.length == 0) { y.push("None"); }
4034 x += addHtmlValue("User Consent", y.join(', '));
4035 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); }
4036 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>';
4037 setDialogMode(2, "Share Device", 1, null, x);
4038 break;
4039 }
4040 case 'getmqttlogin': {
4041 if ((currentNode == null) || (currentNode._id != message.nodeid) || (xxdialogMode != null)) return;
4042 var x = "These settings can be used to connect MQTT for this device." + '<br /><br />';
4043 delete message.action;
4044 delete message.nodeid;
4045 x += '<textarea readonly=readonly style=width:100%;resize:none;height:100px;overflow:auto;font-size:12px readonly>' + JSON.stringify(message) + '</textarea>';
4046 /*
4047 x += addHtmlValue('Username', '<input style=width:230px readonly value="' + message.user + '" />');
4048 x += addHtmlValue('Password', '<input style=width:230px readonly value="' + message.pass + '" />');
4049 x += addHtmlValue('WS URL', '<input style=width:230px readonly value="' + message.wsUrl + '" />');
4050 if (message.mpsUrl && message.mpsCertHash) {
4051 x += addHtmlValue('MPS URL', '<input style=width:230px readonly value="' + message.mpsUrl + '" />');
4052 x += addHtmlValue('MPS Cert Hash', '<input style=width:230px readonly value="' + message.mpsCertHash + '" />');
4053 }
4054 */
4055 setDialogMode(2, "MQTT Credentials", 1, null, x);
4056 break;
4057 }
4058 case 'stopped': { // Server is stopping.
4059 // Disconnect
4060 autoReconnect = false;
4061 QH('p0span', message.msg);
4062 break;
4063 }
4064 case 'updatePluginList': {
4065 installedPluginList = message.list;
4066 updatePluginList();
4067 break;
4068 }
4069 case 'pluginVersionsAvailable': {
4070 if (pluginHandler == null) break;
4071 updatePluginList(message.list);
4072 break;
4073 }
4074 case 'downgradePluginVersions': {
4075 var vSelect = '<select id="lastPluginVersion">';
4076 message.info.versionList.forEach(function(v) { vSelect += '<option value="' + v.zipball_url + '">' + v.name + '</option>'; });
4077 vSelect += '</select>';
4078 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 + '" />');
4079 break;
4080 }
4081 case 'serverBackup': {
4082 if (message.service == 'googleDrive') {
4083 QV('p2ServerActionsGoogleBackup', message.state > 0);
4084 QV('p2ServerActionsGoogleBackupCheck', message.state > 2);
4085 miscState['googleDrive'] = { state: message.state }
4086 if (message.state == 2) {
4087 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 />';
4088 x += addHtmlValue("Activation", '<a href=\"' + message.url + '\" rel="noreferrer noopener" target="_blank">Browse to this URL</a>');
4089 x += addHtmlValue("Code", '<input id=gdcode onkeyup=server_setupGoogleDriveBackupCheck3() style=width:240px></input>');
4090 setDialogMode(2, "Google Drive Backup", 3, server_setupGoogleDriveBackupEx, x, 'gd2');
4091 Q('gdcode').focus();
4092 QE('idx_dlgOkButton', false);
4093 }
4094 }
4095 break;
4096 }
4097 case 'pluginError': {
4098 setDialogMode(2, "Plugin Error", 1, null, message.msg);
4099 break;
4100 }
4101 case 'plugin': {
4102 if ((pluginHandler == null) || (typeof message.plugin != 'string')) break;
4103 try { pluginHandler[message.plugin][message.method](server, message); } catch (e) { console.log('Error loading plugin handler ('+ e + ')'); }
4104 break;
4105 }
4106 case 'amtsetupbin': {
4107 saveAs(new Blob([ Uint8Array.from(atob(message.file), function (c) { return c.charCodeAt(0) }) ], { type: 'application/octet-stream' }), 'setup.bin');
4108 break;
4109 }
4110 case 'previousLogins':{
4111 var tag = 'previousLogins';
4112 if (message.userid != null) { tag += ':' + message.userid; }
4113 if ((xxdialogMode == 2) && (xxdialogTag == tag)) {
4114 var x = '', c = 'BBB', xx = '';
4115 if (message.events.length == 0) {
4116 x += 'No previous login.';
4117 } else {
4118 x += '<div style=max-height:260px;overflow-y:scroll;overflow-x:hidden><table>';
4119 for (var i in message.events) {
4120 var m = message.events[i].m;
4121 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' } }
4122 else if (m == 108) { m = "Invalid 2FA"; c ='DD9DC3'; xx = 'x'; }
4123 else if (m == 109) { m = "Locked account"; c ='E1BBBB'; xx = 'x'; }
4124 else if (m == 110) { m = "Invalid password"; c = 'E1BBBB'; xx = 'x'; }
4125 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>';
4126 }
4127 x += '</table></div>';
4128 }
4129 setDialogMode(2, "Previous Logins", 1, null, x);
4130 }
4131 break;
4132 }
4133 case 'getDeviceDetails': {
4134 saveAs(new Blob([message.data], { type: 'application/octet-stream' }), "devicelist" + '.' + message.type);
4135 break;
4136 }
4137 case 'createLoginToken': { // A new login token was created
4138 if (xxdialogMode) return;
4139 var x = "Take note of this username and password, the password cannot be shown again." + '<br /><br />';
4140 x += addHtmlValue("Name", EscapeHtml(message.name))
4141 if (message.expire != 0) { x += addHtmlValue("Expire", EscapeHtml(printDateTime(new Date(message.expire)))); }
4142 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>');
4143 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>');
4144 setDialogMode(2, "Create Login Token", 1, null, x);
4145 break;
4146 }
4147 case 'loginTokens': { // Reveiced the list of login tokens
4148 loginTokens = message.loginTokens;
4149 mainUpdate(65536);
4150 break;
4151 }
4152 case 'report': {
4153 renderReport(message.data);
4154 break;
4155 }
4156 default:
4157 //console.log('Unknown message.action', message.action);
4158 break;
4159 }
4160 }
4161
4162 // Go to the correct starting view page
4163 function gotoStartViewPage() {
4164 var xviewmode = parseInt('{{viewmode}}');
4165 if (xxcurrentView != -1) return;
4166 if ('{{currentNode}}'.toLowerCase() != '') { // The .toLowerCase here is the minifier will not optimize this out.
4167 if (getNodeFromId('{{currentNode}}') == null) return; // This node is not loaded yet
4168 gotoDevice('{{currentNode}}', xviewmode);
4169 } else if (args.gotonode != null) {
4170 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
4171 if (getNodeFromId('node/' + domain + '/' + args.gotonode) == null) return; // This node is not loaded yet
4172 gotoDevice('node/' + domain + '/' + args.gotonode, xviewmode);
4173 goBackStack.push(1);
4174 } else if (args.gotodevicename != null) {
4175 var foundNode = null;
4176 if (nodes != null) { for (var i in nodes) { if (nodes[i].name == args.gotodevicename) { foundNode = nodes[i]._id; } } }
4177 if (foundNode) { gotoDevice(foundNode, xviewmode); goBackStack.push(1); }
4178 } else if (args.gotodevicername != null) {
4179 var foundNode = null;
4180 if (nodes != null) { for (var i in nodes) { if (nodes[i].rname == args.gotodevicername) { foundNode = nodes[i]._id; } } }
4181 if (foundNode) { gotoDevice(foundNode, xviewmode); goBackStack.push(1); }
4182 } else if (args.gotodeviceip != null) {
4183 var foundNode = null;
4184 if (nodes != null) { for (var i in nodes) { if (nodes[i].ip == args.gotodeviceip) { foundNode = nodes[i]._id; } } }
4185 if (foundNode) { gotoDevice(foundNode, xviewmode); goBackStack.push(1); }
4186 } else if (args.gotomesh != null) {
4187 if (meshes['mesh/' + domain + '/' + args.gotomesh] == null) return; // This device group is not loaded yet
4188 gotoMesh('mesh/' + domain + '/' + args.gotomesh);
4189 go(xviewmode);
4190 goBackStack.push(2);
4191 } else if (args.gotouser != null) {
4192 var xuserid = args.gotouser;
4193 if (args.gotouser.indexOf('/') < 0) { xuserid = 'user/' + domain + '/' + args.gotouser; }
4194 if ((users == null) || (users[xuserid] == null)) return; // This user is not loaded yet
4195 gotoUser(encodeURIComponentEx(xuserid));
4196 go(xviewmode);
4197 goBackStack.push(4);
4198 } else if (args.gotougrp != null) {
4199 var xusergrpid = args.gotougrp;
4200 if (args.gotougrp.indexOf('/') < 0) { xusergrpid = 'ugrp/' + domain + '/' + args.gotougrp; }
4201 if ((usergroups == null) || usergroups[xusergrpid] == null) return; // This user group is not loaded yet
4202 gotoUserGroup(xusergrpid);
4203 go(xviewmode);
4204 goBackStack.push(50);
4205 } else if (!isNaN(xviewmode)) {
4206 go(xviewmode);
4207 } else {
4208 setDialogMode(0);
4209 go(1);
4210 }
4211 delete args.gotonode;
4212 delete args.gotomesh;
4213 delete args.gotouser;
4214 delete args.gotougrp;
4215 }
4216
4217 //
4218 // MY DEVICES
4219 //
4220
4221 function onRealNameCheckBox() {
4222 showRealNames = Q('RealNameCheckBox').checked;
4223 putstore('showRealNames', showRealNames ? 1 : 0);
4224 mainUpdate(7);
4225 }
4226
4227 function onOnlineCheckBox(e) {
4228 putstore('devFilterSelect', Q('DevFilterSelect').value);
4229 onDeviceSearchChanged(e);
4230 }
4231
4232 function updateDevicePageState() {
4233 if ((devicePagingState == null) || (devicePagingState.total <= devicePagingState.limit)) {
4234 QV('devViewPageState', false);
4235 QV('devViewPageButton1', false);
4236 QV('devViewPageButton2', false);
4237 QV('devViewPageButton3', false);
4238 QV('devViewPageButton4', false);
4239 } else {
4240 var currentPage = Math.floor((devicePagingState.skip + devicePagingState.limit) / devicePagingState.limit);
4241 var maxPage = Math.ceil(devicePagingState.total / devicePagingState.limit);
4242 QV('devViewPageState', true);
4243 QV('devViewPageButton1', true);
4244 QV('devViewPageButton2', true);
4245 QV('devViewPageButton3', true);
4246 QV('devViewPageButton4', true);
4247 QH('devViewPageState', currentPage + '/' + maxPage);
4248 }
4249 }
4250
4251 function onDeviceViewPageChange(i) {
4252 if (devicePagingState == null) return;
4253 var currentPage = (Math.floor((devicePagingState.skip + devicePagingState.limit) / devicePagingState.limit));
4254 var maxPage = Math.ceil(devicePagingState.total / devicePagingState.limit);
4255 switch (i) {
4256 case 1: { if (currentPage > 1) meshserver.send({ action: 'nodes', skip: 0 }); break; } // Goto first page
4257 case 2: { if (currentPage > 1) meshserver.send({ action: 'nodes', skip: (currentPage - 2) * devicePagingState.limit }); break; } // Goto previous page
4258 case 3: { if (currentPage < maxPage) meshserver.send({ action: 'nodes', skip: currentPage * devicePagingState.limit }); break; } // Goto next page
4259 case 4: { if (currentPage < maxPage) meshserver.send({ action: 'nodes', skip: (maxPage - 1) * devicePagingState.limit }); break; } // Goto last page
4260 }
4261 }
4262
4263 function onDeviceViewChange(i) {
4264 if (i != null) { Q('viewselect').value = i; }
4265 for (var j = 1; j < 6; j++) { Q('devViewButton' + j).classList.remove('viewSelectorSel'); }
4266 Q('devViewButton' + Q('viewselect').value).classList.add('viewSelectorSel');
4267 putstore('deviceView', Q('viewselect').value);
4268 putstore('viewsize', Q('sizeselect').value);
4269 mainUpdate(4);
4270 setTimeout(function () { mainUpdate(512); }, 200);
4271 }
4272
4273 function onDeviceViewSettings() {
4274 if (xxdialogMode) return;
4275
4276 // Use defaults if needed
4277 if (deviceViewSettings == null) { deviceViewSettings = {}; }
4278 if (!Array.isArray(deviceViewSettings.devsCols)) { deviceViewSettings.devsCols = ['user','ip','conn']; }
4279
4280 // Display the dialog box
4281 var x = '';
4282 x += '<label><input id=d2c8 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('agtype') >= 0)?' checked':'') + '>' + "Agent Type" + '</label><br />';
4283 x += '<label><input id=d2c9 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('agver') >= 0)?' checked':'') + '>' + "Agent Version" + '</label><br />';
4284 x += '<label><input id=d2c6 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('desc') >= 0)?' checked':'') + '>' + "Device Description" + '</label><br />';
4285 x += '<label><input id=d2c5 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('os') >= 0)?' checked':'') + '>' + "Operating System" + '</label><br />';
4286 x += '<label><input id=d2c10 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('tags') >= 0)?' checked':'') + '>' + "Tags" + '</label><br />';
4287 x += '<label><input id=d2c11 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('windowsav') >= 0)?' checked':'') + '>' + "Windows AV" + '</label><br />';
4288 x += '<label><input id=d2c12 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('windowsupdate') >= 0)?' checked':'') + '>' + "Windows Update" + '</label><br />';
4289 x += '<label><input id=d2c13 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('windowsfirewall') >= 0)?' checked':'') + '>' + "Windows Firewall" + '</label><br />';
4290 x += '<label><input id=d2c14 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('lastbootuptime') >= 0)?' checked':'') + '>' + "Last Boot Up Time" + '</label><br />';
4291 x += '<label><input id=d2c16 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('idletime') >= 0)?' checked':'') + '>' + "Idle Time" + '</label><br />';
4292 x += '<label><input id=d2c1 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('links') >= 0)?' checked':'') + '>' + "MeshCentral Router Links" + '</label><br />';
4293 x += '<label><input id=d2c2 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('user') >= 0)?' checked':'') + '>' + "Logged in users" + '</label><br />';
4294 x += '<label><input id=d2c3 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('ip') >= 0)?' checked':'') + '>' + "Agent IP address" + '</label><br />';
4295 x += '<label><input id=d2c4 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('conn') >= 0)?' checked':'') + '>' + "Server Connectivity" + '</label><br />';
4296 x += '<label><input id=d2c15 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('amthost') >= 0)?' checked':'') + '>' + "Intel&reg; AMT hostname" + '</label><br />';
4297 x += '<label><input id=d2c17 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('amtstate') >= 0)?' checked':'') + '>' + "Intel&reg; AMT state" + '</label><br />';
4298 x += '<label><input id=d2c7 type=checkbox' + ((deviceViewSettings.devsCols.indexOf('lastseen') >= 0)?' checked':'') + '>' + "Last Seen" + '</label><br />';
4299 setDialogMode(2, "Device View Columns", 3, onDeviceViewSettingsEx, x);
4300 }
4301
4302 function onDeviceViewSettingsEx() {
4303 var cols = [];
4304 if (Q('d2c1').checked) { cols.push('links'); }
4305 if (Q('d2c2').checked) { cols.push('user'); }
4306 if (Q('d2c3').checked) { cols.push('ip'); }
4307 if (Q('d2c4').checked) { cols.push('conn'); }
4308 if (Q('d2c5').checked) { cols.push('os'); }
4309 if (Q('d2c6').checked) { cols.push('desc'); }
4310 if (Q('d2c7').checked) { cols.push('lastseen'); }
4311 if (Q('d2c8').checked) { cols.push('agtype'); }
4312 if (Q('d2c9').checked) { cols.push('agver'); }
4313 if (Q('d2c10').checked) { cols.push('tags'); }
4314 if (Q('d2c11').checked) { cols.push('windowsav'); }
4315 if (Q('d2c12').checked) { cols.push('windowsupdate'); }
4316 if (Q('d2c13').checked) { cols.push('windowsfirewall'); }
4317 if (Q('d2c14').checked) { cols.push('lastbootuptime'); }
4318 if (Q('d2c15').checked) { cols.push('amthost'); }
4319 if (Q('d2c16').checked) { cols.push('idletime'); }
4320 if (Q('d2c17').checked) { cols.push('amtstate'); }
4321 deviceViewSettings.devsCols = cols;
4322 putstore('_deviceViewSettings', JSON.stringify(deviceViewSettings));
4323 mainUpdate(4);
4324 }
4325
4326 function ondockeypress(e) {
4327 setSessionActivity();
4328 if (!xxdialogMode && (xxcurrentView == 11) && desktop && Q('DeskControl').checked) {
4329 // Check what keys we are allows to send
4330 if (currentNode != null) {
4331 var meshrights = GetNodeRights(currentNode);
4332 var inputAllowed = ((features2 & 0x2000) == 0) && ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
4333 if (inputAllowed == false) return false;
4334 var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
4335 if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; }
4336 }
4337 return desktop.m.handleKeys(e);
4338 }
4339 if (!xxdialogMode && (xxcurrentView == 12) && terminal && (terminal.State == 3) && (xterm == null)) { return terminal.m.TermHandleKeys(e); }
4340 if (!xxdialogMode && ((xxcurrentView == 15) || (xxcurrentView == 115))) return agentConsoleHandleKeys(e);
4341 if (!xxdialogMode && xxcurrentView == 4) {
4342 if ((e.ctrlKey == true) || (e.altKey == true) || (e.metaKey == true)) return;
4343 var processed = 0;
4344 if (e.key) {
4345 if ((e.key.length === 1) && (userSearchFocus == 0)) { Q('UserSearchInput').value = ((Q('UserSearchInput').value + e.key)); processed = 1; }
4346 if ((e.keyCode == 8) && (userSearchFocus == 0)) { var x = Q('UserSearchInput').value; Q('UserSearchInput').value = x.substring(0, x.length - 1); processed = 1; }
4347 if (e.keyCode == 27) { Q('UserSearchInput').value = ''; processed = 1; }
4348 } else {
4349 if (e.charCode != 0 && userSearchFocus == 0) { Q('UserSearchInput').value = ((Q('UserSearchInput').value + String.fromCharCode(e.charCode))); processed = 1; }
4350 }
4351 if (processed > 0) { if (processed == 1) { onUserSearchInputChanged(); } return haltEvent(e); }
4352 }
4353 if (xxdialogMode || xxcurrentView != 1) return;
4354 if (e.ctrlKey == true && e.charCode == 96) {
4355 showRealNames = !showRealNames;
4356 Q('RealNameCheckBox').value = showRealNames;
4357 putstore('showRealNames', showRealNames ? 1 : 0);
4358 mainUpdate(6)
4359 return;
4360 }
4361 if (e.ctrlKey == true || e.altKey == true || e.metaKey == true) return;
4362 if (Q('viewselect').value < 4) {
4363 var processed = 0;
4364 if (e.key) {
4365 if ((e.key.length === 1) && (searchFocus == 0)) { Q('KvmSearchInput').value = Q('SearchInput').value = ((Q('SearchInput').value + e.key)); processed = 1; }
4366 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; }
4367 if (e.keyCode == 27) { Q('KvmSearchInput').value = Q('SearchInput').value = ''; processed = 1; }
4368 } else {
4369 if (e.charCode != 0 && searchFocus == 0) { Q('KvmSearchInput').value = Q('SearchInput').value = ((Q('SearchInput').value + String.fromCharCode(e.charCode))); processed = 1; }
4370 }
4371 if (processed > 0) { if (processed == 1) { mainUpdate(5); } return haltEvent(e); }
4372 }
4373 if (Q('viewselect').value == 4) {
4374 if (e.key) {
4375 if ((e.key.length === 1) && (mapSearchFocus == 0)) { Q('mapSearchLocation').value = ((Q('mapSearchLocation').value + e.key)); processed = 1; }
4376 //if (e.keyCode == 8 && mapSearchFocus == 0) { var x = Q('mapSearchLocation').value; Q('mapSearchLocation').value = x.substring(0, x.length - 1); processed = 1; }
4377 if (e.keyCode == 27) { Q('mapSearchLocation').value = ''; mapCloseSearchWindow(); processed = 1; }
4378 if (e.keyCode == 13) { getSearchLocation(); }
4379 } else {
4380 if (e.charCode != 0 && mapSearchFocus == 0) { Q('mapSearchLocation').value = ((Q('mapSearchLocation').value + String.fromCharCode(e.charCode))); processed = 1; }
4381 }
4382 }
4383 }
4384
4385 function ondockeydown(e) {
4386 setSessionActivity();
4387 if (!xxdialogMode && (xxcurrentView == 11) && desktop && Q('DeskControl').checked) {
4388 // Check what keys we are allows to send
4389 if (currentNode != null) {
4390 var meshrights = GetNodeRights(currentNode);
4391 var inputAllowed = ((features2 & 0x2000) == 0) && ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
4392 if (inputAllowed == false) return false;
4393 var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
4394 if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; }
4395 }
4396 return desktop.m.handleKeyDown(e);
4397 }
4398 if (!xxdialogMode && (xxcurrentView == 12) && terminal && (terminal.State == 3) && xterm == null) { terminal.m.TermHandleKeyDown(e); if ((e.keyCode >= 37) && (e.keyCode <= 40)) { haltEvent(e); } }
4399 if (!xxdialogMode && (xxcurrentView == 13) && (e.keyCode == 116) && (p13filetree != null)) { haltEvent(e); return false; } // F5 Refresh on files
4400 if (!xxdialogMode && ((xxcurrentView == 15) || (xxcurrentView == 115))) { return agentConsoleHandleKeys(e); }
4401 if (!xxdialogMode && (xxcurrentView == 4)) {
4402 if ((e.keyCode === 8) && (userSearchFocus == 0)) { var x = Q('UserSearchInput').value; Q('UserSearchInput').value = (x.substring(0, x.length - 1)); processed = 1; }
4403 if (e.keyCode === 27) { Q('UserSearchInput').value = ''; processed = 1; }
4404 if (processed > 0) { if (processed == 1) { mainUpdate(5); } return haltEvent(e); }
4405 }
4406 if (xxdialogMode || (xxcurrentView != 1) || (e.ctrlKey == true) || (e.altKey == true) || (e.metaKey == true)) return;
4407 var processed = 0;
4408 if (Q('viewselect').value < 4) {
4409 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; }
4410 if (e.keyCode === 27) { Q('KvmSearchInput').value = Q('SearchInput').value = ''; processed = 1; }
4411 if (processed > 0) { if (processed == 1) { mainUpdate(5); } return haltEvent(e); }
4412 }
4413 if (Q('viewselect').value == 4) {
4414 if ((e.keyCode === 8) && (mapSearchFocus == 0)) { var x = Q('mapSearchLocation').value; Q('mapSearchLocation').value = (x.substring(0, x.length - 1)); processed = 1; }
4415 if (e.keyCode === 27) { Q('mapSearchLocation').value = ''; mapCloseSearchWindow(); processed = 1; }
4416 }
4417 }
4418
4419 function ondockeyup(e) {
4420 setSessionActivity();
4421 if (!xxdialogMode && (xxcurrentView == 11) && desktop && Q('DeskControl').checked) {
4422 // Check what keys we are allows to send
4423 if (currentNode != null) {
4424 var meshrights = GetNodeRights(currentNode);
4425 var inputAllowed = ((features2 & 0x2000) == 0) && ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0)));
4426 if (inputAllowed == false) return false;
4427 var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0)));
4428 if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; }
4429 }
4430 return desktop.m.handleKeyUp(e);
4431 }
4432 if (!xxdialogMode && (xxcurrentView == 12) && terminal && (terminal.State == 3) && xterm == null) { return terminal.m.TermHandleKeyUp(e); }
4433 if (!xxdialogMode && (xxcurrentView == 13) && (e.keyCode == 116) && (p13filetree != null)) { p13folderup(9999); haltEvent(e); return false; } // F5 Refresh on files
4434 if (!xxdialogMode && (xxcurrentView == 4)) { if (((e.keyCode === 8) && (searchFocus == 0)) || e.keyCode === 27) { return haltEvent(e); } }
4435 if (xxdialogMode && (e.keyCode == 27)) { dialogclose(0); }
4436 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
4437 if (xxdialogMode || xxcurrentView != 0 || e.ctrlKey == true || (e.altKey == true) || (e.metaKey == true)) return;
4438 if (Q('viewselect').value < 4) { if (((e.keyCode === 8) && (searchFocus == 0)) || (e.keyCode === 27)) { return haltEvent(e); } }
4439 if (Q('viewselect').value == 4) { if (((e.keyCode === 8) && (mapSearchFocus == 0)) || (e.keyCode === 27)) { return haltEvent(e); } }
4440 }
4441
4442 //function ondocfocus() { }
4443 // TODO: Add handleReleaseKeys() for Intel AMT.
4444 function ondocblur() { if (!xxdialogMode && xxcurrentView == 11 && desktop && Q('DeskControl').checked && desktop.m.handleReleaseKeys) { return desktop.m.handleReleaseKeys(); } }
4445
4446 // Highlights the device group hovered
4447 function devGrpMouseHover(element, over) {
4448 setSessionActivity();
4449 var view = Q('viewselect').value;
4450 var e = element.children[1].children[1];
4451 e.children[0].classList.remove('g1s');
4452 e.children[1].classList.remove('e2s');
4453 e.children[2].classList.remove('g2s');
4454 if (over == 1) {
4455 e.children[0].classList.add('g1s');
4456 e.children[1].classList.add('e2s');
4457 e.children[2].classList.add('g2s');
4458 }
4459 }
4460
4461 // Highlights the device being hovered
4462 function devMouseHover(element, over) {
4463 setSessionActivity();
4464 var view = Q('viewselect').value;
4465 if (view == 1) {
4466 var e = element.children[0].children[0].children[1].children[0].children[0].children[0];
4467 e.children[1].classList.remove('g1s');
4468 e.children[2].classList.remove('e2s');
4469 e.children[3].classList.remove('g2s');
4470 if (over == 1) {
4471 e.children[1].classList.add('g1s');
4472 e.children[2].classList.add('e2s');
4473 e.children[3].classList.add('g2s');
4474 }
4475 } else if (view == 2) {
4476 var e = element;
4477 e.children[2].classList.remove('g1s');
4478 e.children[4].classList.remove('e2s');
4479 e.children[3].classList.remove('g2s');
4480 if (over == 1) {
4481 e.children[2].classList.add('g1s');
4482 e.children[4].classList.add('e2s');
4483 e.children[3].classList.add('g2s');
4484 }
4485 }
4486 }
4487
4488 var deviceHeaderId = 0;
4489 var deviceHeaderTotal = 0;
4490 var deviceHeadersTitles = {};
4491 var deviceHeaderCount;
4492 var deviceHeaders = {};
4493 var oldviewmode = 0;
4494 function updateDevices() {
4495 if ((nodes == null) || (xxcurrentView != 1)) { return; }
4496 var r = '', c = 0, current = null, count = 0, scount = 0, displayedMeshes = {}, view = Q('viewselect').value, groups = {}, groupCount = {};
4497 QV('xdevices', view != 4);
4498 QV('xdevicesmap', view == 4);
4499 QV('devListToolbar', view < 3);
4500 QV('kvmListToolbar', (view == 3) || (view == 5));
4501 QV('devMapToolbar', view == 4);
4502 QV('devListToolbarSize', (view == 3) || (view == 5));
4503 QV('devListToolbarSettings', view == 2);
4504 QV('NoMeshesPanel', (nodes.length == 0) && (meshcount == 0));
4505 //QV('devListToolbarView', (meshcount != 0) && (nodes.length > 0));
4506 QV('devListToolbarViewIcons', nodes.length > 0);
4507 QV('devListToolbarSort', (nodes.length > 0) && (view != 4));
4508 if (nodes.length == 0) { view = 1; sort = 0; }
4509 if (view == 4) {
4510 setTimeout( function() { if (xxmap.map != null) { xxmap.map.updateSize(); } }, 200);
4511 // TODO
4512 } else {
4513 // 3 wide, list view or desktop view
4514 deviceHeaderId = 0;
4515 deviceHeaderCount = {};
4516 deviceHeaderTotal = 0;
4517 deviceHeaders = {};
4518 deviceHeadersTitles = {};
4519 var kvmDivs = [];
4520
4521 // Perform node sort
4522 if (sort == 0) { nodes.sort(meshSort); }
4523 else if (sort == 1) { nodes.sort(powerSort); }
4524 else if (sort == 2) { if (showRealNames == true) { nodes.sort(deviceHostSort); } else { nodes.sort(deviceSort); } }
4525 else if (sort == 5) {
4526 // If the last seen column is not turned on, turn it on first (we require this to sort the data)
4527 if (!(deviceViewSettings && deviceViewSettings.devsCols && deviceViewSettings.devsCols.indexOf('lastseen') >= 0)) {
4528 // Request last connection data if not requested yet
4529 if (requestedLastConnects == false) { requestedLastConnects = true; meshserver.send({ action: 'lastconnects' }); }
4530 // Force initialize the view settings
4531 if (deviceViewSettings == null) { deviceViewSettings = {}; }
4532 if (!Array.isArray(deviceViewSettings.devsCols)) { deviceViewSettings.devsCols = ['user','ip','conn', 'lastseen']; }
4533 else { deviceViewSettings.devsCols.push('lastseen'); }
4534 }
4535 nodes.sort(lastConnectSort);
4536 }
4537 else if (sort == 6) {
4538 // Last Boot Up Time
4539 if (!(deviceViewSettings && deviceViewSettings.devsCols && deviceViewSettings.devsCols.indexOf('lastbootuptime') >= 0)) {
4540 // Force initialize the view settings
4541 if (deviceViewSettings == null) { deviceViewSettings = {}; }
4542 if (!Array.isArray(deviceViewSettings.devsCols)) { deviceViewSettings.devsCols = ['user','ip','conn', 'lastbootuptime']; }
4543 else { deviceViewSettings.devsCols.push('lastbootuptime'); }
4544 }
4545 nodes.sort(lastBootUpTimeSort);
4546 }
4547
4548 // Compute the width of the device view.
4549 var totalDeviceViewWidth = Q('column_l').clientWidth - 60;
4550 var deviceBoxWidth = Math.floor(totalDeviceViewWidth / 301);
4551 deviceBoxWidth = 301 + Math.floor((totalDeviceViewWidth - (deviceBoxWidth * 301)) / deviceBoxWidth);
4552
4553 // Compute number of colums
4554 var colcount = 4;
4555 if (deviceViewSettings && deviceViewSettings.devsCols) {
4556 colcount = deviceViewSettings.devsCols.length + 1;
4557 if (deviceViewSettings.devsCols.indexOf('desc') >= 0) { colcount--; } // Description is not an extra column.
4558 }
4559
4560 // Go thru the list of nodes and display them
4561 for (var i in nodes) {
4562 var node = nodes[i];
4563 if (node.v == false) continue;
4564 var mesh2 = meshes[node.meshid];
4565 //if ((view == 3) && (mesh2.mtype == 1)) continue;
4566 var meshrights = GetNodeRights(node);
4567 if (sort == 0) {
4568 // Mesh header
4569 if (((meshes[node.meshid]?node.meshid:'*') != current)) {
4570 if (((view == 1) || (view == 3) || (view == 5)) && (current != null)) { r += '</div>'; } // Close collapse div
4571 deviceHeaderSet();
4572 var extra = '';
4573 if (view == 2) { r += '<tr><td colspan=' + colcount + '>'; }
4574 if (meshes[node.meshid] && (meshes[node.meshid].mtype == 1)) { extra = '<span class=devHeaderx>' + ", Intel&reg; AMT only" + '</span>'; }
4575 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>'; } }
4576 if ((view == 1) && (current != null)) { if (c == 2) { r += '<td><div style=width:301px></div></td>'; } if (r != '') { r += '</tr></table>'; } }
4577 if (view == 2) { r += '<div>'; }
4578 r += '<div class=DevSt style=width:100%;padding-top:4px><span style=float:right>';
4579 r += '<span id=DevxHeader' + deviceHeaderId + ' class=devHeaderx></span>' + extra;
4580 r += '</span>';
4581 if ((view == 1) || (view == 2) || (view == 3) || (view == 5)) {
4582 var collapsed = CollapsedGroups[node.meshid];
4583 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
4584 }
4585 if (meshes[node.meshid]) {
4586 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>';
4587 current = node.meshid;
4588 } else {
4589 r += '<span id=MxMESH><i>' + "Individual Devices" + '</i></span></div>';
4590 current = '*';
4591 }
4592 if (view == 2) { r += '</div>'; }
4593 displayedMeshes[current] = 1;
4594 c = 0;
4595 if ((view == 1) || (view == 3) || (view == 5)) { r += '<div id=DevxCol' + deviceHeaderId + ((collapsed === true)?' style=display:none':'') + '>'; } // Open collapse div
4596 }
4597 } else if (sort == 1) {
4598 // Power header
4599 var pwr = node.pwr?node.pwr:0;
4600 if (pwr !== current) {
4601 if (((view == 1) || (view == 3) || (view == 5)) && (current != null)) { r += '</div>'; } // Close collapse div
4602 deviceHeaderSet();
4603 if ((view == 1) && (current !== null)) { if (c == 2) { r += '<td><div style=width:301px></div></td>'; } if (r != '') { r += '</tr></table>'; } }
4604
4605 if (view == 2) { r += '<tr><td colspan=' + colcount + '>'; }
4606 r += '<div class=DevSt style=width:100%;padding-top:4px><span id=DevxHeader' + deviceHeaderId + ' class=devHeaderx style=float:right></span>';
4607 if ((view == 1) || (view == 2) || (view == 3) || (view == 5)) {
4608 var collapsed = CollapsedGroups['pwr:' + pwr];
4609 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
4610 }
4611 r += '<span>' + PowerStateStr2(node.pwr) + '</span></div>';
4612
4613 current = pwr;
4614 c = 0;
4615 if ((view == 1) || (view == 3) || (view == 5)) { r += '<div id=DevxCol' + deviceHeaderId + ((collapsed === true)?' style=display:none':'') + '>'; } // Open collapse div
4616 }
4617 } else if (sort == 2 || sort == 5 || sort == 6) {
4618 // Device header
4619 if (current == null) { current = '1'; }
4620 }
4621
4622 count++;
4623 if (view == 1) {
4624 // Draw the device standin
4625 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>';
4626 } else if (view == 2) {
4627 // Draw the device standin
4628 var collapseName = node.meshid;
4629 if (sort == 1) { collapseName = ('pwr:' + (node.pwr?node.pwr:0)); }
4630 else if ((sort == 3) || (sort == 4)) { collapseName = 'tag:**xx**xx*TaG*xx**xx**'; }
4631 var collapsed = (sort != 3) & (sort != 4) & CollapsedGroups[collapseName];
4632 r += '<tr name=xxdevice2 colname=DevxCol' + collapseName + ' style=height:20px' + (collapsed?';display:none':'') + ' id=xv2' + node._id + '></tr>';
4633 } 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.
4634 // Draw the device (TODO: See if we can replace this with a standin in the future)
4635 if ((Object.keys(checkedNodeids).length == 0) || checkedNodeids[node._id]) {
4636 r += updateDeviceViewHtml(view, null, node);
4637 kvmDivs.push(node._id);
4638 }
4639 }
4640
4641 // If we are displaying devices by tags, put the device in the right tag group.
4642 if (((sort == 3) || (sort == 4)) && (r != '')) {
4643 if (node.tags) {
4644 for (var j in node.tags) {
4645 var tag = node.tags[j];
4646 if (sort == 4) { if (mesh2) { tag = mesh2.name + ' - ' + node.tags[j]; } else { tag = '**INDV*~*DEVS** - ' + node.tags[j]; } }
4647 var collapsed = CollapsedGroups['tag:' + encodeURIComponentEx(tag)];
4648 var r2 = r.replace('**xx**xx*TaG*xx**xx**', encodeURIComponentEx(tag) + (collapsed?' style=display:none':''));
4649 if (groups[tag] == null) { groups[tag] = r2; groupCount[tag] = 1; } else { groups[tag] += r2; groupCount[tag] += 1; }
4650 if ((view == 3) || (view == 5)) break;
4651 }
4652 } else {
4653 // If sorted by "Groups-Tags" and a device has no tags, put in a group only section.
4654 if ((sort == 4) && (mesh2 != null) && ((node.tags == null) || (node.tags.length == 0))) {
4655 var tag = mesh2.name;
4656 var collapsed = CollapsedGroups['tag:' + encodeURIComponentEx(tag)];
4657 var r2 = r.replace('**xx**xx*TaG*xx**xx**', encodeURIComponentEx(tag) + (collapsed?' style=display:none':''));
4658 if (groups[tag] == null) { groups[tag] = r2; groupCount[tag] = 1; } else { groups[tag] += r2; groupCount[tag] += 1; }
4659 }
4660 }
4661 r = '';
4662 }
4663
4664 deviceHeaderTotal++;
4665 if (typeof deviceHeaderCount[node.state] == 'undefined') { deviceHeaderCount[node.state] = 1; } else { deviceHeaderCount[node.state]++; }
4666 }
4667
4668 if ((view == 1) && (current != null)) { r += '</div>'; } // Close collapse div
4669
4670 // Above 32 devices, gray out the auto connect feature.
4671 if (kvmDivs.length >= 32) { Q('autoConnectDesktopCheckbox').checked = false; }
4672 QE('autoConnectDesktopCheckbox', kvmDivs.length < 32);
4673
4674 // If displaying devices by groups, sort the group names and display the devices.
4675 if ((sort == 3) || (sort == 4)) {
4676 var groupNames = [], tagDeviceHeaderId = 0;
4677 for (var i in groups) { groupNames.push(i); }
4678 //groupNames.sort(function (a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); });
4679 groupNames.sort(function (a, b) { return sortCollator.compare(a.toLowerCase(), b.toLowerCase()); });
4680 for (var j in groupNames) {
4681 var i = groupNames[j];
4682 if (view == 2) {
4683 r += '<tr><td colspan=' + colcount + '><div class=DevSt style=width:100%;padding-top:4px>';
4684 var collapsed = CollapsedGroups['tag:' + encodeURIComponentEx(i)];
4685 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
4686 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];
4687 } else {
4688 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>';
4689 var collapsed = CollapsedGroups['tag:' + encodeURIComponentEx(i)];
4690 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
4691 r += '<span>' + EscapeHtml(i).split('|').join(' &rarr; ').split('**INDV*~*DEVS**').join('<i>' + "Individual Devices" + '</i>') + '</span></div>';
4692 r += '<div id=DevxCol' + tagDeviceHeaderId + ((collapsed === true)?' style=display:none':'') + '>' + groups[i] + '</div>'; // Open collapse div
4693 }
4694 tagDeviceHeaderId++;
4695 }
4696 }
4697
4698 // If there is nothing to display, explain the problem
4699 var viewNothing = false;
4700 if ((r == '') && (nodes.length > 0) && ((Q('SearchInput').value != '') || (Q('DevFilterSelect').value != 0))) {
4701 viewNothing = true;
4702 if (sort == 3) {
4703 r = '<div style="margin:30px">' + "No devices are included in any groups, click on a device's \"Groups\" to add to a group." + '</div>';
4704 } else {
4705 r = '<div style="margin:30px">' + "No devices matching this search." + ' <a onclick=clearDeviceSearch()>' + "Clear search filter" + '</a></div>';
4706 }
4707 }
4708
4709 if ((view == 1) && (c == 2)) r += '<td><div style=width:301px></div></td>'; // Adds device padding
4710
4711 // Display all empty device groups, we need to do this because users can add devices to these at any time.
4712 if ((sort == 0) && ((Q('SearchInput').value == '') && (Q('DevFilterSelect').value == 0)) && (view < 3)) {
4713 var deviceHeaderId2 = deviceHeaderId, sortedMeshes = [];
4714 for (var i in meshes) { sortedMeshes.push(meshes[i]); }
4715 sortedMeshes.sort(nameSort);
4716 for (var i in sortedMeshes) {
4717 var mesh = sortedMeshes[i], meshrights = GetMeshRights(mesh);
4718 if (displayedMeshes[mesh._id] == null) {
4719 if ((current != '') && (r != '')) { r += '</tr></table>'; }
4720 r += '<table style=width:100%;padding-top:4px cellpadding=0 cellspacing=0><tr><td colspan=3 class=DevSt>';
4721
4722 // Collapsing header & start collapsing area
4723 deviceHeaderId2++;
4724 var collapsed = CollapsedGroups[mesh._id];
4725 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
4726
4727 r += '<span id=MxMESH style=cursor:pointer onclick=gotoMesh("' + mesh._id + '")>' + EscapeHtml(mesh.name) + '</span><span>';
4728 r += getMeshActions(mesh, meshrights);
4729 r += '</span></td></tr><tr>';
4730 if (mesh.mtype == 1) {
4731 r += '<td><div style=padding:10px><i>' + "No Intel&reg; AMT devices in this device group";
4732 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>'; }
4733 } else if (mesh.mtype == 2) {
4734 r += '<td><div id=DevxCol' + deviceHeaderId2 + ((collapsed === true)?' style=display:none':'') + '>'; // Open collapse div
4735 r += '<div style=padding:10px><i>' + "No devices in this device group";
4736 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>'; }
4737 } else if (mesh.mtype == 3) {
4738 r += '<td><div id=DevxCol' + deviceHeaderId2 + ((collapsed === true)?' style=display:none':'') + '>'; // Open collapse div
4739 r += '<div style=padding:10px><i>' + "No local devices in this device group";
4740 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>'; }
4741 } else if (mesh.mtype == 4) {
4742 r += '<td><div id=DevxCol' + deviceHeaderId2 + ((collapsed === true)?' style=display:none':'') + '>'; // Open collapse div
4743 r += '<div style=padding:10px><i>' + "No devices in this device group";
4744 }
4745 r += '.</i></div></td>';
4746 r += '</div>'; // End collapsing area
4747
4748 current = mesh._id;
4749 count++;
4750 }
4751 }
4752 }
4753
4754 if ((r != '') && (viewNothing == false)) {
4755 r += '</table>';
4756 if (view == 2) {
4757 var colums = '';
4758
4759 // Use defaults if needed
4760 if (deviceViewSettings == null) { deviceViewSettings = {}; }
4761 if (!Array.isArray(deviceViewSettings.devsCols)) { deviceViewSettings.devsCols = ['user','ip','conn']; }
4762
4763 // Display configured columns
4764 if (deviceViewSettings.devsCols.indexOf('agtype') >= 0) { colums += '<th style=color:gray;width:100px>' + "Agent Type"; }
4765 if (deviceViewSettings.devsCols.indexOf('agver') >= 0) { colums += '<th style=color:gray;width:100px>' + "Agent Version"; }
4766 if (deviceViewSettings.devsCols.indexOf('os') >= 0) { colums += '<th style=color:gray;width:160px>' + "OS"; }
4767 if (deviceViewSettings.devsCols.indexOf('tags') >= 0) { colums += '<th style=color:gray;width:120px>' + "Tags"; }
4768 if (deviceViewSettings.devsCols.indexOf('windowsav') >= 0) { colums += '<th style=color:gray;width:100px>' + "Windows AV"; }
4769 if (deviceViewSettings.devsCols.indexOf('windowsupdate') >= 0) { colums += '<th style=color:gray;width:120px>' + "Windows Update"; }
4770 if (deviceViewSettings.devsCols.indexOf('windowsfirewall') >= 0) { colums += '<th style=color:gray;width:120px>' + "Windows Firewall"; }
4771 if (deviceViewSettings.devsCols.indexOf('lastbootuptime') >= 0) { colums += '<th style=color:gray;width:120px>' + "Last Boot Up Time"; }
4772 if (deviceViewSettings.devsCols.indexOf('idletime') >= 0) { colums += '<th style=color:gray;width:100px title="' + "Updated every 5 Minutes" + '">' + "Idle Time"; }
4773 if (deviceViewSettings.devsCols.indexOf('links') >= 0) { colums += '<th style=color:gray;width:120px>' + "Links"; }
4774 if(features3 & 0x00000002){
4775 if (deviceViewSettings.devsCols.indexOf('user') >= 0) { colums += '<th style=color:gray;width:180px>' + "User"; }
4776 } else {
4777 if (deviceViewSettings.devsCols.indexOf('user') >= 0) { colums += '<th style=color:gray;width:120px>' + "User"; }
4778 }
4779 if (deviceViewSettings.devsCols.indexOf('ip') >= 0) { colums += '<th style=color:gray;width:120px>' + "Address"; }
4780 if (deviceViewSettings.devsCols.indexOf('conn') >= 0) { colums += '<th style=color:gray;width:100px>' + "Connectivity"; }
4781 if (deviceViewSettings.devsCols.indexOf('amthost') >= 0) { colums += '<th style=color:gray;width:100px>' + "AMT Host"; }
4782 if (deviceViewSettings.devsCols.indexOf('amtstate') >= 0) { colums += '<th style=color:gray;width:100px>' + "AMT State"; }
4783 if (deviceViewSettings.devsCols.indexOf('lastseen') >= 0) {
4784 colums += '<th style=color:gray;width:120px>' + "Last Seen";
4785 if (requestedLastConnects == false) { requestedLastConnects = true; meshserver.send({ action: 'lastconnects' }); }
4786 }
4787
4788 // This height of 1 div at the end to fix a problem in Linux firefox browsers
4789 r = '<table style=width:100%;margin-top:4px;table-layout:fixed cellpadding=0 cellspacing=0><th style=color:gray>' + colums + r + '</tr></table><div style=height:1px></div>';
4790 }
4791 } else {
4792 r += '</table>';
4793 if (sort == 3) { r = '<div style="margin:10px"><i>' + "No devices with tags found." + '</i></div>'; }
4794 }
4795
4796 // Add a "Add Device Group" option
4797 r += '<div style=border-top-style:solid;border-top-width:1px;border-top-color:#DDDDDD;cursor:pointer;font-size:small;margin-top:4px>';
4798 if ((view < 3) && (sort == 0) && (Object.keys(meshes).length > 0) && ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 64) == 0))) {
4799 r += '<a href=# onclick="return account_createMesh()" title="' + "Create a new group of devices." + '" style=cursor:pointer>' + "Add Device Group" + '</a>&nbsp';
4800 }
4801 if ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 128) == 0)) {
4802 r += '<a href=# onclick=\'return p10showMeshCmdDialog(0)\' style=cursor:pointer title="' + "Download MeshCmd, a command line tool that performs many functions." + '">' + "MeshCmd" + '</a>&nbsp';
4803 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'; }
4804 }
4805 r += '</div><br/>';
4806
4807 QH('xdevices', r);
4808 deviceHeaderSet();
4809
4810 for (var i in deviceHeaders) { QH(i, deviceHeaders[i]); }
4811 for (var i in deviceHeadersTitles) { Q(i).title = deviceHeadersTitles[i]; }
4812 p1updateInfo();
4813
4814 // Take care of KVM surfaces in desktop view mode
4815 if (((view == 3) || (view == 5)) && (xxcurrentView == 1)) {
4816
4817 // Figure out and adjust the size to fill the width of the div
4818 var vsize = [{ x: 180, y: 101 }, { x: 302, y: 169 }, { x: 454, y: 255 }][Q('sizeselect').selectedIndex];
4819 if ((view == 3)) { // If we are in fixed width mode, correct the size for the screen width.
4820 var realw = vsize.x + 2, tw = totalDeviceViewWidth - 5, xw = Math.floor(tw / realw);
4821 xw = realw + Math.floor((tw - (xw * realw)) / xw);
4822 vsize.y = vsize.y * (xw / vsize.x);
4823 vsize.x = xw;
4824 }
4825
4826 for (var i in multiDesktop) { multiDesktop[i].xxdelete = true; }
4827 for (var i in kvmDivs) {
4828 var id = kvmDivs[i], shortid = id.split('/')[2], desk = multiDesktop[id];
4829 if (desk != null) {
4830 // This device already has a canvas, use it.
4831 var width = (view == 5)?((desk.m.width * vsize.y) / desk.m.height):vsize.x;
4832 desk.m.CanvasId.setAttribute('style', 'background-color:black;width:' + width + 'px;height:' + vsize.y + 'px');
4833 Q('xkvmid_' + shortid).appendChild(desk.m.CanvasId);
4834 delete desk.xxdelete;
4835 QH('skvmid_' + shortid, ["Disconnected", "Connecting...", "Setup...", '', ''][((desk.m.State == null)?desk.m.state:desk.m.State)]);
4836 } else {
4837 var node = getNodeFromId(id);
4838 if ((desktopNode == node) && (desktop != null)) { // Check if the main desktop is this device, if it is, use that.
4839 // This device already has a canvas, use it.
4840 var c = desktop.m.CanvasId;
4841 var width = (view == 5)?((desktop.m.width * vsize.y) / desktop.m.height):vsize.x;
4842 c.setAttribute('id', 'kvmid_' + shortid);
4843 c.setAttribute('style', 'background-color:black;width:' + width + 'px;height:' + vsize.y + 'px');
4844 c.setAttribute('onclick', 'toggleKvmDevice(\'' + id + '\')');
4845 c.removeAttribute('onmousedown');
4846 c.removeAttribute('onmouseup');
4847 c.removeAttribute('onmousemove');
4848 Q('xkvmid_' + shortid).appendChild(c);
4849 QH('skvmid_' + shortid, ["Disconnected", "Connecting...", "Setup...", '', ''][((desktop.m.State == null)?desktop.m.state:desktop.m.State)]);
4850 if (desktop.m.SendCompressionLevel) { desktop.m.SendCompressionLevel(multidesktopsettings.agentencoding, multidesktopsettings.quality, multidesktopsettings.scaling, multidesktopsettings.framerate); }
4851 desktop.shortid = shortid;
4852 desktop.onStateChanged = onMultiDesktopStateChange;
4853 desktop.m.onRemoteInputLockChanged = null;
4854 desktop.m.onKeyboardStateChanged = null;
4855 multiDesktop[id] = desktop;
4856 desktop = desktopNode = currentNode = null;
4857 // Setup a replacement desktop
4858 QH('DeskParent', '<canvas id="Desk" oncontextmenu="return false" onmousedown=dmousedown(event) onmouseup=dmouseup(event) onmousemove=dmousemove(event)></canvas>');
4859 } else {
4860 // This is a new device, create a canvas for it.
4861 var c = document.createElement('canvas');
4862 c.setAttribute('id', 'kvmid_' + shortid);
4863 c.setAttribute('width', 640);
4864 c.setAttribute('height', 480);
4865 c.setAttribute('oncontextmenu', 'return false');
4866 c.setAttribute('style', 'background-color:black;width:' + vsize.x + 'px;height:' + vsize.y + 'px');
4867 c.setAttribute('onclick', 'toggleKvmDevice(\'' + id + '\')');
4868 try { Q('xkvmid_' + shortid).appendChild(c); } catch (ex) {}
4869 // Check if we need to auto-connect
4870 if (Q('autoConnectDesktopCheckbox').checked == true) { setTimeout(function() { connectMultiDesktop(node, 1); }, 100); }
4871 }
4872 }
4873 }
4874 for (var i in multiDesktop) {
4875 // If a device is no longer viewed, disconnect it.
4876 if (multiDesktop[i].xxdelete == true) { multiDesktop[i].Stop(); delete multiDesktop[i]; }
4877 else if (debugmode && multiDesktop[i].m && multiDesktop[i].m.onScreenSizeChange) {
4878 mdeskAdjust(multiDesktop[i].m, multiDesktop[i].m.ScreenWidth, multiDesktop[i].m.ScreenHeight, multiDesktop[i].m.CanvasId); // Adjust screen size change
4879 }
4880 }
4881 deskAdjust();
4882 } else {
4883 disconnectAllKvmFunction();
4884 Q('autoConnectDesktopCheckbox').checked = false;
4885 }
4886 }
4887 oldviewmode = view;
4888 onDevicesScrollEx();
4889 }
4890
4891
4892 var onDevicesTouchActive = false;
4893 var onDevicesScrollnagleTimer = null;
4894 function onDevicesScroll(top) {
4895 if(typeof top == 'undefined') top = false;
4896 if(top) Q('xdevices').scrollTop = 0;
4897 if ((onDevicesScrollnagleTimer != null) || (onDevicesTouchActive)) return;
4898 onDevicesScrollnagleTimer = setTimeout(onDevicesScrollEx, 250);
4899 }
4900
4901 function onDeviceTouch(x) {
4902 if (onDevicesTouchActive == x) return;
4903 onDevicesTouchActive = x;
4904 if (x == false) onDevicesScrollEx();
4905 }
4906
4907 function onDevicesScrollEx() {
4908 if (onDevicesScrollnagleTimer != null) { clearTimeout(onDevicesScrollnagleTimer); onDevicesScrollnagleTimer = null; }
4909 var visibleTop = Q('xdevices').scrollTop - 200, visibleBottom = Q('xdevices').scrollTop + Q('xdevices').clientHeight + 200;
4910 var view = Q('viewselect').value, devdivs = document.getElementsByName('xxdevice' + view);
4911 for (var i = 0; i < devdivs.length; i++) {
4912 if ((devdivs[i].offsetTop >= visibleTop) && (devdivs[i].offsetTop < visibleBottom)) {
4913 var node = getNodeFromId(devdivs[i].id.substring(3));
4914 if (node != null) { updateDeviceViewHtml(view, devdivs[i], node); } else { devdivs[i].innerHTML = ''; }
4915 } else {
4916 devdivs[i].innerHTML = '';
4917 }
4918 }
4919 }
4920
4921 // Update a single device in the current view
4922 function updateDeviceViewDevice(node) {
4923 var view = Q('viewselect').value;
4924 if ((view != 1) && (view != 2)) { mainUpdate(4); return; }
4925 if (typeof node == 'string') { node = getNodeFromId(node); }
4926 if (node == null) return;
4927 var devdiv = Q('xv' + view + node._id);
4928 if ((devdiv != null) && (devdiv.innerHTML != '')) { updateDeviceViewHtml(view, devdiv, node); } // Only update if the device is visible
4929 }
4930
4931 function updateDeviceViewHtml(view, div, node) {
4932 var title = EscapeHtml(node.name);
4933 if (title.length == 0) { title = '<i>' + "None" + '</i>'; }
4934 if ((node.rname != null) && (node.rname.length > 0)) { title += ' / ' + EscapeHtml(node.rname); }
4935 var name = EscapeHtml(node.name);
4936 if (showRealNames == true && node.rname != null) name = EscapeHtml(node.rname);
4937 if (name.length == 0) { name = '<i>' + "None" + '</i>'; }
4938 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
4939
4940 // Add device notification icons
4941 var devNotify = '', devNotifySub = '';
4942
4943 // This device is "starred"
4944 if (stars[node._id] == 1) {
4945 if (view == 2) {
4946 devNotifySub += '<img class=deviceNotifySmallDotSub src=images/icon-star-notify-10.png width=10 height=10>';
4947 } else {
4948 devNotifySub += '<img class=deviceNotifyDotSub src=images/icon-star-notify-16.png width=16 height=16>';
4949 }
4950 }
4951
4952 // This device has session information
4953 if (node.sessions != null) {
4954 // Display any agent messages
4955 if (node.sessions.msg != null) {
4956 if (view == 2) {
4957 devNotifySub += '<div onclick=showDeviceMessages(\'' + node._id + '\',null,event) style="width:10;height:10" class=deviceNotifySmallDotSub></div>';
4958 } else {
4959 devNotifySub += '<div onclick=showDeviceMessages(\'' + node._id + '\',null,event) style="width:16;height:16" class=deviceNotifyDotSub>' + Object.keys(node.sessions.msg).length + '</div>';
4960 }
4961 }
4962
4963 // Sessions are active
4964 if ((node.sessions.kvm != null) || (node.sessions.terminal != null) || (node.sessions.files != null) || (node.sessions.tcp != null) || (node.sessions.udp != null)) {
4965 if (view == 2) {
4966 devNotifySub += '<img onclick=showDeviceSessions(\'' + node._id + '\',null,event) class=deviceNotifySmallDotSub src=images/icon-relay-notify10.png width=10 height=10>';
4967 } else {
4968 devNotifySub += '<img onclick=showDeviceSessions(\'' + node._id + '\',null,event) class=deviceNotifyDotSub src=images/icon-relay-notify.png width=16 height=16>';
4969 }
4970 }
4971
4972 // Help is required
4973 if (node.sessions.help != null) {
4974 if (view == 2) {
4975 devNotifySub += '<img onclick=showDeviceHelpRequests(\'' + node._id + '\',null,event) class=deviceNotifySmallDotSub src=images/icon-help-notify-10.png width=10 height=10>';
4976 } else {
4977 devNotifySub += '<img onclick=showDeviceHelpRequests(\'' + node._id + '\',null,event) class=deviceNotifyDotSub src=images/icon-help-notify-16.png width=16 height=16>';
4978 }
4979 }
4980
4981 // Battery state
4982 if ((node.sessions.battery != null) && (view == 1)) {
4983 var bat = node.sessions.battery;
4984 var statestr = '';
4985 if (bat.state == 'ac') { statestr = "Device is plugged-in"; }
4986 if (bat.state == 'dc') { statestr = "Device is battery powered"; }
4987
4988 var levelstr = '', levelnum = -1;
4989 if ((typeof bat.level == 'number') && (bat.level >= 0) && (bat.level <= 100)) {
4990 levelstr = bat.level + '%';
4991 levelnum = (Math.floor((bat.level + 10) / 25) + 1);
4992 if (levelnum > 5) { lvl = 5; }
4993 if (bat.state == 'ac') { if (bat.level == 100) { levelnum = 11; } else { levelnum += 5; } }
4994 }
4995
4996 if (levelnum > 0) {
4997 devNotify += '<div class="deviceBatterySmall deviceBatterySmall' + levelnum + '" title="' + ((statestr != null)?(statestr + ', ' + levelstr):levelstr) + '"></div>';
4998 }
4999 }
5000 }
Showing first 5,000 of 20,674 lines. View raw