<!DOCTYPE html> <html lang="en" dir="ltr" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta content="text/html; charset=utf-8" http-equiv="Content-Type" /> <meta name="viewport" content="user-scalable=1.0,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0" /> <meta name="format-detection" content="telephone=no" /> <meta name="robots" content="noindex,nofollow"> <link rel="manifest" href="{{{domainurl}}}manifest.json"> <link rel="shortcut icon" href="{{{domainurl}}}favicon.ico" /> <link rel="icon" type="image/png" sizes="16x16" href="{{{domainurl}}}favicon-16x16.png"> <link rel="icon" type="image/png" sizes="32x32" href="{{{domainurl}}}favicon-32x32.png"> <link rel="apple-touch-icon" href="/favicon-303x303.png" /> <link type="text/css" href="styles/xterm.css" media="screen" rel="stylesheet" title="CSS" /> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="#ffffff"> <meta name="apple-mobile-web-app-title" content="{{{title}}}"> <script type="text/javascript" src="scripts/common-0.0.1{{{min}}}.js"></script> <script type="text/javascript" src="scripts/meshcentral{{{min}}}.js"></script> <script type="text/javascript" src="scripts/agent-redir-ws-0.1.1{{{min}}}.js"></script> <script type="text/javascript" src="scripts/agent-desktop-0.0.2{{{min}}}.js"></script> <script type="text/javascript" src="scripts/amt-0.2.0{{{min}}}.js"></script> <script type="text/javascript" src="scripts/amt-redir-ws-0.1.0{{{min}}}.js"></script> <script type="text/javascript" src="scripts/amt-desktop-0.0.2{{{min}}}.js"></script> <script type="text/javascript" src="scripts/xterm{{{min}}}.js"></script> <script type="text/javascript" src="scripts/xterm-addon-fit{{{min}}}.js"></script> <script type="text/javascript" src="scripts/zlib{{{min}}}.js"></script> <script type="text/javascript" src="scripts/zlib-inflate{{{min}}}.js"></script> <script type="text/javascript" src="scripts/zlib-adler32{{{min}}}.js"></script> <script type="text/javascript" src="scripts/zlib-crc32{{{min}}}.js"></script> <script keeplink=1 type="text/javascript" src="scripts/filesaver.min.js"></script> <meta name="msapplication-TileColor" content="#00aba9"> <meta name="theme-color" content="#ffffff"> <title>{{{title}}}</title> <style> body { background-color: white; } .night body { background-color: black; } #MxMESH { color: black; } .night #MxMESH { color: lightgray; } .textOverGray { color: black; } #dialog { z-index:1000; background-color:#EEE; box-shadow:0px 0px 15px #666; font-family:Arial,Helvetica,sans-serif; border-radius:5px; position:fixed; top:90px; width:300px; } .night #dialog { color: black; background-color:#AAA; } :focus { outline: 0; } a { color: #036; text-decoration: underline; } .night a { color: #99F; } #footer a { color: #fff; text-decoration: underline; } #footer a:hover { text-decoration: none; } .night #footer { color: gray; } .i1 { background: url(../images/icons50.png) 0px 0px; background-image: image-set(url(../images/icons50.png) 1x, url(../images/icons100.png) 2x); height: 50px; width: 50px; border: none; } .i2 { background: url(../images/icons50.png) -50px 0px; background-image: image-set(url(../images/icons50.png) 1x, url(../images/icons100.png) 2x); height: 50px; width: 50px; border: none; } .i3 { background: url(../images/icons50.png) -100px 0px; background-image: image-set(url(../images/icons50.png) 1x, url(../images/icons100.png) 2x); height: 50px; width: 50px; border: none; } .i4 { background: url(../images/icons50.png) -150px 0px; background-image: image-set(url(../images/icons50.png) 1x, url(../images/icons100.png) 2x); height: 50px; width: 50px; border: none; } .i5 { background: url(../images/icons50.png) -200px 0px; background-image: image-set(url(../images/icons50.png) 1x, url(../images/icons100.png) 2x); height: 50px; width: 50px; border: none; } .i6 { background: url(../images/icons50.png) -250px 0px; background-image: image-set(url(../images/icons50.png) 1x, url(../images/icons100.png) 2x); height: 50px; width: 50px; border: none; } .i7 { background: url(../images/icons50.png) -300px 0px; background-image: image-set(url(../images/icons50.png) 1x, url(../images/icons100.png) 2x); height: 50px; width: 50px; border: none; } .i8 { background: url(../images/icons50.png) -350px 0px; background-image: image-set(url(../images/icons50.png) 1x, url(../images/icons100.png) 2x); height: 50px; width: 50px; border: none; } .m0 { background: url(../images/images16.png) -32px 0px; height: 16px; width: 16px; border: none; float: left; } .m1 { background: url(../images/images16.png) -16px 0px; height: 16px; width: 16px; border: none; float: left; } .m2 { background: url(../images/images16.png) -96px 0px; height: 16px; width: 16px; border: none; float: left; } .m3 { background: url(../images/images16.png) -112px 0px; height: 16px; width: 16px; border: none; float: left; } .m4 { background: url(../images/images16.png) -128px 0px; height: 16px; width: 16px; border: none; float: left; } .NotifyIconSmall1 { width:24px; height:24px; background: url(../images/notify24.png) 0px 0px; background-image: image-set(url(../images/notify24.png) 1x, url(../images/notify48.png) 2x); } .NotifyIconSmall2 { width:24px; height:24px; background: url(../images/notify24.png) -24px 0px; background-image: image-set(url(../images/notify24.png) 1x, url(../images/notify48.png) 2x); } .NotifyIconSmall3 { width:24px; height:24px; background: url(../images/notify24.png) -48px 0px; background-image: image-set(url(../images/notify24.png) 1x, url(../images/notify48.png) 2x); } .NotifyIconSmall4 { width:24px; height:24px; background: url(../images/notify24.png) -72px 0px; background-image: image-set(url(../images/notify24.png) 1x, url(../images/notify48.png) 2x); } .NotifyIconSmall5 { width:24px; height:24px; background: url(../images/notify24.png) -96px 0px; background-image: image-set(url(../images/notify24.png) 1x, url(../images/notify48.png) 2x); } .NotifyIconSmall6 { width:24px; height:24px; background: url(../images/notify24.png) -120px 0px; background-image: image-set(url(../images/notify24.png) 1x, url(../images/notify48.png) 2x); } .NotifyIconSmall7 { width:24px; height:24px; background: url(../images/notify24.png) -144px 0px; background-image: image-set(url(../images/notify24.png) 1x, url(../images/notify48.png) 2x); } .NotifyIconSmall8 { width:24px; height:24px; background: url(../images/notify24.png) -168px 0px; background-image: image-set(url(../images/notify24.png) 1x, url(../images/notify48.png) 2x); } .NotifyIconSmall9 { width:24px; height:24px; background: url(../images/notify24.png) -192px 0px; background-image: image-set(url(../images/notify24.png) 1x, url(../images/notify48.png) 2x); } .gray { /*filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#grayscale");*/ /* Firefox 10+, Firefox on Android */ filter: gray; /* IE6-9 */ -webkit-filter: grayscale(100%) opacity(60%); /* Chrome 19+, Safari 6+, Safari 6+ iOS */ } .DevSt { padding-left: 5px; border-bottom-style: solid; border-bottom-width: 1px; border-bottom-color: #DDDDDD; } .noselect { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .fileIcon1 { background: url(data:image/gif;base64,R0lGODlhEAAQAJEDAPb49Y2Sj9LT2f///yH5BAEAAAMALAAAAAAQABAAAAImnI+py+1vhJwyUYAzHTL4D3qdlJWaIFJqmKod607sDKIiDUP63hQAOw==); height: 16px; width: 16px; cursor: pointer; border: none; float: left; margin-top: 1px; } .fileIcon2 { background: url(data:image/gif;base64,R0lGODlhEAAQAJEDAM2xV/Xur+XPgP///yH5BAEAAAMALAAAAAAQABAAAAJD3ISZIGHWUGihznesYDYATFVM+D2hJ4lgN1olxALAtAlmPCJvuMmJd6PJckDYwicrHhTD5o7plJmg0Uc0asNMkphHAQA7); height: 16px; width: 16px; cursor: pointer; border: none; float: left; margin-top: 1px; } .fileIcon3 { background: url(data:image/gif;base64,R0lGODlhEAAQAJEDAPb19IGBgbq6uv///yH5BAEAAAMALAAAAAAQABAAAAIy3ISpxgcPH2ouQgFEw1YmxnUXKEaaEZZnVWZk66JwzKpvuwZzwOgwb/C1gIOA8Yg8DgoAOw==); height: 16px; width: 16px; cursor: pointer; border: none; float: left; margin-top: 1px; } .fileIcon4 { background: url(../images/meshicon16.png); height: 16px; width: 16px; cursor: pointer; border: none; float: left; margin-top: 1px; } .filelist { -moz-user-select: none; -khtml-user-select: none; -webkit-user-select: none; -o-user-select: none; cursor: default; -khtml-user-drag: element; clear: both; } .deviceNotifyDot { position:absolute; right:10px; top:0px; height:16px; } .deviceNotifyDotSub { text-align:center; color:#FFF; width:16px; background-color:#00F; padding:2px; border-radius:10px; box-shadow: 2px 2px 10px black; cursor:pointer; margin-left:3px; float:left; } .deviceNotifyDotSub:hover { background-color:#44F; } .deviceNotifySmallDot { position:absolute; right:10px; top:0px; height:10px; } .deviceNotifySmallDotSub { text-align:center; color:#FFF; width:10px; padding:2px; background-color:#00F; border-radius:10px; box-shadow: 2px 2px 10px black; cursor:pointer; margin-left:2px; float:left; } .deviceNotifySmallDotSub:hover { background-color:#44F; } .deviceNotifyLargeDot { position:absolute; right:10px; top:10px; height:40px; } .deviceNotifyLargeDotSub { text-align:center; width:35px; height:35px; color:#FFF; padding:2px; background-color:#00F; border-radius:20px; box-shadow: 2px 2px 10px black; cursor:pointer; margin-left:4px; font-size:30px; float:left; } .deviceNotifyLargeDotSub:hover { background-color:#44F; } .style10 { background-color: #C9C9C9; color: #000; } .night .style10 { background-color: #888; } .deviceBatteryLarge { position:absolute; right:10px; top:0px; width:28px; height:48px; border:none; box-shadow:none; } .deviceBatteryLarge1 { background: url(../images/batteries48.png) 0px 0px; } .deviceBatteryLarge2 { background: url(../images/batteries48.png) -28px 0px; } .deviceBatteryLarge3 { background: url(../images/batteries48.png) -56px 0px; } .deviceBatteryLarge4 { background: url(../images/batteries48.png) -84px 0px; } .deviceBatteryLarge5 { background: url(../images/batteries48.png) -112px 0px; } .deviceBatteryLarge6 { background: url(../images/batteries48.png) -140px 0px; } .deviceBatteryLarge7 { background: url(../images/batteries48.png) -168px 0px; } .deviceBatteryLarge8 { background: url(../images/batteries48.png) -196px 0px; } .deviceBatteryLarge9 { background: url(../images/batteries48.png) -224px 0px; } .deviceBatteryLarge10 { background: url(../images/batteries48.png) -252px 0px; } .deviceBatteryLarge11 { background: url(../images/batteries48.png) -280px 0px; } .deviceBatterySmall { position:absolute; left:6px; top:22px; width:14px; height:24px; border:none; box-shadow:none; } .deviceBatterySmall1 { background: url(../images/batteries24.png) 0px 0px; } .deviceBatterySmall2 { background: url(../images/batteries24.png) -14px 0px; } .deviceBatterySmall3 { background: url(../images/batteries24.png) -28px 0px; } .deviceBatterySmall4 { background: url(../images/batteries24.png) -42px 0px; } .deviceBatterySmall5 { background: url(../images/batteries24.png) -56px 0px; } .deviceBatterySmall6 { background: url(../images/batteries24.png) -70px 0px; } .deviceBatterySmall7 { background: url(../images/batteries24.png) -84px 0px; } .deviceBatterySmall8 { background: url(../images/batteries24.png) -98px 0px; } .deviceBatterySmall9 { background: url(../images/batteries24.png) -112px 0px; } .deviceBatterySmall10 { background: url(../images/batteries24.png) -126px 0px; } .deviceBatterySmall11 { background: url(../images/batteries24.png) -140px 0px; } .meshList { width:auto; height:40px; background-color:lightgray; margin-top:5px; margin-bottom:5px; margin-left:60px; padding-top:5px; padding-bottom:5px; border-radius:8px 0px 0px 8px; } .night .meshList { background-color: gray; } .devList1 { height: 50px; cursor: pointer; position: relative; margin-top: 5px; margin-bottom: 5px; } .devList2 { float: left; margin-left: 4px } .devList3 { width: auto; height: 40px; background-color: lightgray; margin-left: 60px; padding-top: 5px; padding-bottom: 5px; border-radius: 8px 0px 0px 8px; } .night .devList3 { background-color: gray; } .devList4 { padding-left: 12px; padding-top: 2px; color: black; } .devList5 { padding-left: 12px; padding-top: 3px; color: #444 } .night .devList5 { color: black; } .deskButton { box-shadow: 0px 0px 10px #000; border-radius:20px; position:absolute; right:10px; top:10px; cursor:pointer; background-color:#AAA; z-index:1000; } .menuButton{ box-shadow: 0px 0px 10px #000; border-radius:10px; display:inline-block; width:120px; background-color:#AAA; text-align:center; padding:8px; cursor:pointer; margin:10px; z-index:1000; } #notificationCount { min-width: 28px; font-size: 20px; background-color: orange; text-align: center; cursor: pointer; color: black; } .notifiyBox { font-size: 16px; position: absolute; z-index: 1000; top: 60px; right: 76px; width: 300px; text-align: left; background-color: #F0ECCD; border: 4px solid #666; -webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius: 10px; -webkit-box-shadow: 2px 2px 4px #888; -moz-box-shadow: 2px 2px 4px #888; box-shadow: 2px 2px 4px #888; max-height: 200px; } .night .notifiyBox { color: black; } .notifiyBox:before { content: ' '; position: absolute; width: 0; height: 0; right: 5px; top: -30px; border: 15px solid; border-color: transparent #666 #666 transparent; } .notifiyBox:after { content: ' '; position: absolute; width: 0; height: 0; right: 7px; top: -24px; border: 12px solid; border-color: transparent #F0ECCD #F0ECCD transparent; } #p15statetext { padding: 4px; height: 15px; } #p15agentConsole { background: black; margin: 0; padding: 0; color: lightgray; width: 100%; position: relative; } #p15coreName { padding: 4px; display: inline-block; } #p15agentConsoleText { position:absolute; margin: 0; padding: 0; top: 0; bottom: 0; left:0; right: 0; overflow-y: scroll; overflow-x: auto; } .areaHead { padding-top: 2px; padding-bottom: 2px; background: #C0C0C0; } .night .areaHead { color: #CCC; background: #333; } .areaFoot { padding-top: 2px; padding-bottom: 2px; background: #C0C0C0; } .night .areaFoot { color: #CCC; background: #333; } .toright2 { float: right; text-align: right; } #consoleTable { width: 100%; height: 100%; padding: 0px; margin-top: 0px; } .night #consoleTable { color: black; } .menucurve { background-color:white; width:10px; height:10px; border-radius:10px 0 0 0; border-right:1px solid white; border-bottom:1px solid white; } .night .menucurve { background-color:black; border-right:1px solid black; border-bottom:1px solid black; } #termTable { width: 100%; padding: 0px; margin-top: 0px; } .fulldesk #termTable { position: absolute; top: 0; bottom: 0; left: 0; right: 0; } #termarea3x { background: black; text-align: center; height: 400px; position: relative; } </style> </head> <body id="body" onload="if (typeof(startup) !== 'undefined') startup();" style="overflow-y:hidden;margin:0;padding:0;border:0;font-size:13px;font-family:\'Trebuchet MS\', Arial, Helvetica, sans-serif"> <div id=container> <div id="notifiyBox" class="notifiyBox" style="display:none"></div> <div id=mastheadx></div> <div id=masthead style="background:url(logo.png) 0px 0px;background-size:341px 50px;background-color:#036;background-repeat:no-repeat;height:50px;width:100%;overflow:hidden"> <div style="width:calc(100% - 50px);overflow:hidden"> <div style="float:left;height:66px;color:#c8c8c8;padding-left:10px;padding-top:6px"> <strong><font style="font-size:36px;font-family:Arial,Helvetica,sans-serif">{{{title1}}}</font></strong> </div> <div style="float:left;height:66px;color:#c8c8c8;padding-left:5px;padding-top:10px"> <strong><font style="font-size:12px;font-family:Arial,Helvetica,sans-serif">{{{title2}}}</font></strong> </div> </div> <div id=notificationCount onclick="clickNotificationIcon()" class="unselectable" style="position:absolute;right:50px;top:0px;font-size:28px;width:50px;height:50px;cursor:pointer;display:none" title="Click to view current notifications"><div id="notificationCount2" style="padding-top:8px">0</div></div> <img id="topMenuIcon" class=noselect style="position:absolute;right:0;top:10px;color:#c8c8c8;font-size:44px;margin-right:8px;cursor:pointer;display:none" onclick=topMenu() src="/images/3bars-30.png" width=30 height=30 /> </div> <div id=page_content style="position:absolute;bottom:32px;top:50px;width:100%"> <div id=column_l style="width:100%;padding:0;position:absolute;bottom:0px;top:0px"> <div id=p0 style=display:none;width:100%;height:100%> <div style="display:flex;align-items:center;width:100%;height:100%"> <div id=p0message style=text-align:center;width:100%><span id="p0span">Server disconnected</span>, <href onclick=reload() style=cursor:pointer><u>click to reconnect</u></href>.</div> </div> </div> <div id=p1 style=display:none;width:100%;height:100%> <div style="display:flex;align-items:center;width:100%;height:100%"> <div id=p1message style=text-align:center;width:100%></div> </div> </div> <div id=p2 style="display:none;position:absolute;top:0;left:0;right:0;bottom:0"> <div id=xdevices style="position:absolute;overflow-y:auto;top:0;left:0;right:0;bottom:34px" onscroll="onDevicesScroll()" ontouchstart="onDeviceTouch(true)" ontouchend="onDeviceTouch(false)"></div> <div id=xdevicesBar style="position:absolute;overflow-y:auto;height:34px;left:0;right:0;bottom:0px;background-color:#aaa;color:black"> <div style="margin:4px"> <span style="width:20px;display:inline-block;text-align:center;cursor:pointer;font-size:16px" onclick=clearSearchInput()><b>X</b></span> <input id=SearchInput autocomplete=off type=search placeholder=Filter onchange=onDeviceSearchChanged(event) onclick=onDeviceSearchChanged(event) onkeyup=onDeviceSearchChanged(event) style="padding:2px;margin:0;height:20px;background-color:#FFF" /> <label class=noselect><input type=checkbox id=RealNameCheckBox onclick=onRealNameCheckBox() />OS Name</label> <label class=noselect><input type=checkbox id=OnlineCheckBox onclick=onOnlineCheckBox(event) />Online</label> </div> </div> </div> <div id=p3 style=display:none;position:absolute;bottom:0;top:0;width:100%> <table cellspacing=0 style="margin:0;padding:0;border-spacing:0;border:0"> <tr style=padding:0> <td style="padding:0;color:#c8c8c8;text-align:center;cursor:pointer" width=60px valign=top onclick=goBack()> <div style="padding:0;background-color:#036;width:10px;height:10px;float:right;border:0"> <div class="menucurve"></div> </div> <div style="padding:0;font-size:25px;background-color:#036;width:50px;border-radius:0 0 10px 0;height:36px">◀</div> </td> <td> <div style=margin-left:5px> <strong style="font-size:large"><span id=p3userName></span></strong><br /> </div> </td> </tr> </table> <div id=p3info style="overflow-y:auto;position:absolute;top:55px;bottom:0px;width:100%"> <img id="p2AccountImage" alt="" loading="lazy" width="128" height="128" onclick="account_manageImage(0)" src="images/user-256.png" style="position:absolute;right:8px;top:7px;border-radius:8px;box-shadow:0px 0px 7px #000" /> <div style="margin-left:8px"> <div id="p3AccountActions"> <div id="p2AccountSecurity" style="display:none"> <p><strong>Account Security</strong></p> <div style="margin-left:9px;margin-bottom:8px"> <div id="managePhoneNumber1" style="margin-top:5px;display:none"><a onclick="account_managePhone()" style="cursor:pointer">Manage phone number</a> <span id="authPhoneNumberCheck"><strong>✓</strong></span></div> <div id="manageEmail2FA" style="margin-top:5px;display:none"><a onclick="account_manageAuthEmail()" style="cursor:pointer">Manage email authentication</a> <span id="authEmailSetupCheck"><strong>✓</strong></span></div> <div id="manageAuthApp" style="margin-top:5px;display:none"><a onclick="account_manageAuthApp()" style="cursor:pointer">Manage authenticator app</a> <span id="authAppSetupCheck"><strong>✓</strong></span></div> <div id="manageOtp" style="margin-top:5px;display:none"><a onclick="account_manageOtp(0)" style="cursor:pointer">Manage backup codes</a> <span id="authCodesSetupCheck"><strong>✓</strong></span></div> <div style="margin-top:5px"><a href=# onclick="return account_viewPreviousLogins()">View previous logins</a></div> </div> </div> <div id="p2AccountActions" style="display:none"> <p><strong>Account Actions</strong></p> <div style="margin-left:9px;margin-bottom:8px"> <div style="margin-top:5px"><span id="managePhoneNumber2" style="display:none"><a onclick="account_managePhone()" style="cursor:pointer">Manage phone number</a></span></div> <div style="margin-top:5px"><span id="verifyEmailId" style="display:none"><a onclick="account_showVerifyEmail()" style="cursor:pointer">Verify email</a></span></div> <div style="margin-top:5px"><span id="changeEmailId" style="display:none"><a onclick="account_showChangeEmail()" style="cursor:pointer">Change email address</a></span></div> <div style="margin-top:5px"><a onclick="account_showChangePassword()" style="cursor:pointer">Change password</a><span id="p2nextPasswordUpdateTime"></span></div> <div style="margin-top:5px"><a onclick="account_showDeleteAccount()" style="cursor:pointer">Delete account</a></div> <div style="margin-top:5px"><a onclick="toggleNightMode()" style="cursor:pointer">Set dark mode</a></div> <div style="margin-top:5px"><a onclick="showNotes(false)" style="cursor:pointer">Personal notes</a></div> </div> <br style=clear:both /> </div> </div> <strong>Device Groups</strong> <span id="p3createMeshLink1">( <a onclick=account_createMesh() style=cursor:pointer><img src="images/icon-addnew.png" width=12 height=12 border=0 /> New</a> )</span> <br /><br /> <div id=p3meshes></div> <div id=p3noMeshFound style=margin-left:9px;display:none>No device groups.<span id="p3createMeshLink2"> <a onclick=account_createMesh() style=cursor:pointer><strong>Get started here!</strong></a></span></div> <br style=clear:both /> </div> </div> </div> <div id=p5 style=display:none> <table cellspacing=0 style="margin:0;padding:0;border-spacing:0;border:0;"> <tr style=padding:0> <td style="padding:0;color:#c8c8c8;text-align:center;cursor:pointer" width=60px valign=top onclick=goBack()> <div style="padding:0;background-color:#036;width:10px;height:10px;float:right;border:0"> <div class="menucurve"></div> </div> <div style="padding:0;font-size:25px;background-color:#036;width:50px;border-radius:0 0 10px 0;height:36px">◀</div> </td> <td> <img src="/images/user-50.png" width=50 height=50 /> </td> <td> <div style=margin-left:5px> <strong style="font-size:large">My Files</strong><br /> </div> </td> </tr> </table> <div id=p5myfiles style="position:absolute;top:55px;bottom:0px;width:100%"> <table id="p5toolbar" style="width:100%;height:78px" cellpadding="0" cellspacing="0"> <tr> <td style="width:100%;background-color:#d3d9d6;text-align:left;padding:4px" valign=bottom> <div style="width:100%;text-align:center"> <input type=button style="width:calc(100%/5 - 5px)" id=p5FolderUp disabled="disabled" onclick="p5folderup()" value="Up" /> <input type=button style="width:calc(100%/5 - 5px)" id=p5SelectAllButton disabled="disabled" onclick="p5selectallfile()" value="SelectAll" onkeypress="return false" onkeydown="return false" /> <input type=button style="width:calc(100%/5 - 5px)" id=p5RenameFileButton disabled="disabled" value="Rename" onclick="p5renamefile()" onkeypress="return false" onkeydown="return false" /> <input type=button style="width:calc(100%/5 - 5px)" id=p5DeleteFileButton disabled="disabled" value="Delete" onclick="p5deletefile()" onkeypress="return false" onkeydown="return false" /> <input type=button style="width:calc(100%/5 - 5px)" id=p5NewFolderButton disabled="disabled" value="Folder" onclick="p5createfolder()" onkeypress="return false" onkeydown="return false" /> </div> <div style="width:100%;text-align:center"> <input type=button style="width:calc(100%/5 - 5px)" id=p5UploadButton disabled="disabled" value="Upload" onclick="p5uploadFile()" onkeypress="return false" onkeydown="return false" /> <input type=button style="width:calc(100%/5 - 5px)" id=p5CutButton disabled="disabled" value="Cut" onclick="p5copyFile(1)" onkeypress="return false" onkeydown="return false" /> <input type=button style="width:calc(100%/5 - 5px)" id=p5CopyButton disabled="disabled" value="Copy" onclick="p5copyFile(0)" onkeypress="return false" onkeydown="return false" /> <input type=button style="width:calc(100%/5 - 5px)" id=p5PasteButton disabled="disabled" value="Paste" onclick="p5pasteFile()" onkeypress="return false" onkeydown="return false" /> <input type=button style="width:calc(100%/5 - 5px)" id=p5RefreshButton value="Refresh" onclick="p5refreshFiles()" onkeypress="return false" onkeydown="return false" /> </div> </td> </tr> <tr> <td style="background-color:#E4E9E7;height:28px"> <table style="width:100%"> <tr> <td id=p5currentpath style="overflow:hidden;padding-left:4px;padding-top:2px;color:black"></td> <td style="text-align:right;padding-right:4px"> <select id=p5sortdropdown onchange=updateFiles()> <option value=1 selected="selected">Sort by name</option> <option value=2>Sort by size</option> <option value=3>Sort by date</option> <option value=4>Descend by name</option> <option value=5>Descend by size</option> <option value=6>Descend by date</option> </select> </td> </tr> </table> </td> </tr> </table> <div id="p5filetable" style="width:100%;height:calc(100% - 102px);overflow:auto;-webkit-user-select:none"> <!-- <div id="p5bigok" style="width:256px;overflow:hidden;position:absolute;left:337px;top:200px;text-align:center;font-size:1600%;color:#AAAAAA;display:none"><b>✓</b></div> <div id="p5bigfail" style="width:256px;overflow:hidden;position:absolute;left:337px;top:200px;text-align:center;font-size:1600%;color:#AAAAAA;display:none"><b>✗</b></div> --> <span id="p5files"></span> </div> <table id="p5toolbarBottom" style="width:100%;height:22px;position:absolute;bottom:0px;background-color:#D3D9D6" cellpadding=0 cellspacing=0> <tr> <td style="text-align:left;padding:3px"> <span id="p5bottomstatus"></span></td> <td id="p5rightOfButtons" style="text-align:right;padding:3px"></td> </tr> </table> </div> </div> <div id=p10 style=display:none;position:absolute;bottom:0;top:0;width:100%;overflow:hidden> <table id=p10deskTopTable cellspacing=0 style="margin:0;padding:0;border-spacing:0;border:0;position:absolute;top:0"> <tr style=padding:0> <td style="padding:0;color:#c8c8c8;text-align:center;cursor:pointer" width=60px valign=top onclick=goBack()> <div style="padding:0;background-color:#036;width:10px;height:10px;float:right;border:0"> <div class="menucurve"></div> </div> <div style="padding:0;font-size:25px;background-color:#036;width:50px;border-radius:0 0 10px 0;height:36px">◀</div> </td> <td> <a id=MainComputerImage style=cursor:pointer onclick=p10showiconselector()></a> </td> <td> <div style=margin-left:5px> <strong><span id=p10deviceName></span></strong><br /> <span id=MainComputerState></span> </div> </td> </tr> </table> <div id=p10dialog style="z-index:1000;background-color:#EEE;box-shadow:0px 0px 15px #666;font-family:Arial,Helvetica,sans-serif;border-radius:5px;position:fixed;top:30px;width:300px;left:30px;display:none"> <div style="width:100%;background-color:#003366;color:#FFF;border-radius:5px 5px 0 0"> <div style=padding:5px>Keyboard Shortcuts Customization</div> <div style=width:100%;margin:6px></div> </div> <div style="margin-right:16px;margin-left:8px"><div id=p10dialog2 style="margin:auto;margin:3px"></div></div> <div style="padding:10px;margin-bottom:20px"><input type="button" value="OK" style="float:right;width:80px" onclick="deskCustomizeKeysEx()"></div> </div> <div id=p10general style="overflow-y:scroll;position:absolute;top:55px;bottom:0px;width:100%"> <div class="deviceNotifyLargeDot"> <img id="p10deviceStar" class=deviceNotifyLargeDotSub src=images/icon-star-notify-40.png width=35 height=35> <div id="p10deviceMsg" onclick=showDeviceMessages(null,null,event) class=deviceNotifyLargeDotSub></div> <img id="p10deviceNotify" onclick=showDeviceSessions() class=deviceNotifyLargeDotSub src=images/icon-relay-notify-40.png width=35 height=35> <img id="p10deviceHelp" onclick=showDeviceHelpRequests(null,null,event) class=deviceNotifyLargeDotSub src=images/icon-help-notify-40.png width=35 height=35> </div> <div id="p10deviceBattery" class="deviceBatteryLarge deviceBatteryLarge1"></div> <div id=p10html style="margin-left:8px;margin-right:8px"></div> <div id=p10html2></div> <div id=p10html3></div> </div> <img id="deskkeybutton1" src="images/mobile-desk-exit.png" class="deskButton" style="top:10px;display:none" onclick="exitButton()" /> <img id="deskkeybutton3a" src="images/mobile-desk-menu-open.png" class="deskButton" style="top:60px;display:none" onclick="toggleMenu(false)" /> <img id="deskkeybutton3b" src="images/mobile-desk-menu-close.png" class="deskButton" style="top:60px;display:none" onclick="toggleMenu(true)" /> <img id="deskkeybutton4a" src="images/mobile-desk-mouse-left.png" class="deskButton" style="top:110px;display:none" onclick="deskChangeMouseButton(0)" /> <img id="deskkeybutton4b" src="images/mobile-desk-mouse-right.png" class="deskButton" style="top:110px;display:none" onclick="deskChangeMouseButton(1)" /> <img id="deskkeybutton5a" src="images/mobile-desk-scale-out.png" class="deskButton" style="top:160px;display:none" onclick="deskChangeFullscreenZoom()" /> <img id="deskkeybutton5b" src="images/mobile-desk-scale-in.png" class="deskButton" style="top:160px;display:none" onclick="deskChangeFullscreenZoom()" /> <img id="deskkeybutton2a" src="images/mobile-desk-keyboard-open.png" class="deskButton" style="top:210px;display:none" onclick="toggleKeyboard()" /> <img id="deskkeybutton2b" src="images/mobile-desk-keyboard-close.png" class="deskButton" style="top:210px;display:none" onclick="toggleKeyboard()" /> <div style="position:absolute;top:0;left:0;z-index:200;opacity:0;width:1px;height:1px"> <input id="softKeyboard" autocapitalize="off" autocomplete="off" type="text" spellcheck="false" style="z-index:200;opacity:0;width:1px;height:1px" onfocus="keyboardFocusChange()" onblur="keyboardFocusChange()" /> </div> <div id="deskButtonMenu" style="display:none;position:absolute;top:10px;left:10px;right:55px;bottom:10px;z-index:1000"></div> <div id=p10desktop style="overflow:hidden;position:absolute;top:55px;bottom:0px;width:100%;display:none"> <div id=deskarea1 style="position:absolute;top:0px;width:100%;height:32px"> <div style="padding-top:2px;padding-bottom:2px;background:#C0C0C0;height:32px"> <div style="float:right;text-align:right"> <span id="p14power"></span> <input type=button id=deskFullScreen value="Full Screen" onclick=deskToggleFull(event) onkeypress="return false" onkeydown="return false" disabled="disabled" style="height:28px;margin-right:3px;"> </div> <div style="margin-left:3px"> <input type=button id=connectbutton1 value="Connect" onclick=connectDesktop(event,3) onkeypress="return false" onkeydown="return false" disabled="disabled" style="height:28px"> <input type=button id=connectbutton1h value="HW Connect" onclick=connectDesktop(event,2) onkeypress="return false" onkeydown="return false" disabled="disabled" style="height:28px"> <input type=button id=disconnectbutton1 value="Disconnect" onclick=connectDesktop(event,0) onkeypress="return false" onkeydown="return false" style="height:28px"> <span id="deskstatus" style="color:black">Disconnected</span> </div> </div> </div> <div id=deskarea3 style="position:absolute;top:32px;width:100%;height:calc(100% - 64px);background-color:#000;text-align:center"> <div id=DeskParent style="height:100%"> <canvas id=Desk width=640 height=200 style="width:100%;-ms-touch-action:none;margin-left:0px" oncontextmenu="return false" onmousedown=dmousedown(event) onmouseup=dmouseup(event) onmousemove=dmousemove(event) onmousewheel=dmousewheel(event)></canvas> </div> <div id=p11DeskConsoleMsg style="display:none;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> <div id=p11DeskSessionSelector style="display:none;position:absolute;left:30px;top:17px;right:30px;bottom:17px;overflow-y:auto"></div> </div> <div id=deskarea4 style="position:absolute;bottom:0px;width:100%;height:32px"> <div style=padding-top:2px;padding-bottom:2px;background:#C0C0C0> <div style=float:right;text-align:right> <span id=DeskChatButton><img src='images/icon-chat.png' onclick=deviceChat(event) height=16 width=16 style=padding-top:5px;cursor:pointer /></span> <span id=DeskToastButton><img src='images/icon-notify.png' onclick=deviceToastFunction() height=16 width=16 style=padding-top:5px;cursor:pointer /></span> <!--<input id=DeskToolsButton type=button value=Tools onkeypress="return false" onkeydown="return false" onclick="toggleDeskTools()"> --> </div> <div> <input id="deskActionsBtn" type=button style="margin-left:3px;height:28px" onkeypress="return false" onkeydown="return false" value=Actions onclick=deviceActionFunction() /> <input type="button" value="Settings" onkeypress="return false" onkeydown="return false" onclick="showDesktopSettings()" style="height:28px"> <input type="button" onkeypress="return false" onkeydown="return false" value="Power Actions..." onclick="showPowerActionDlg()" style="display:none;height:28px"> <!--<input type="button" id="DeskSpecialKeys" value="Keys" onkeypress="return false" onkeydown="return false" onclick="sendSpecialKeys()" style="height:28px">--> <input type="button" id="DeskScreens" value="Screens" onkeypress="return false" onkeydown="return false" onclick="deskSelectScreens()" style="display:none;height:28px"> <label><span id="DeskControlSpan" style="display:none"><input id="DeskControl" type="checkbox" onkeypress="return false" onkeydown="return false">Input</span></label> </div> </div> </div> </div> <div id="termButtonMenu" style="display:none;position:absolute;top:10px;left:10px;right:55px;bottom:10px;z-index:1000"></div> <div id=p10terminal style="overflow:hidden;position:absolute;top:55px;bottom:0px;width:100%;display:none;background-color:#333"> <div id=termTable style="position:absolute;top:0;bottom:0;left:0;right:0"> <div id="termarea1"> <div class="areaHead" style="line-height:24px"> <div class="toright2"> <input type=button id=termFullScreen value="Full Screen" onclick=deskToggleFull(event) onkeypress="return false" onkeydown="return false" disabled="disabled" style="height:28px;margin-right:3px;"> <div id="terminalCustomUpperRight" style="float:left;margin-right:6px"></div> </div> <div> <span id="connectbutton2span" style="margin-left:3px"><input type="button" id="connectbutton2" cmenu="termConnectButton" value="Connect" style="height:28px" onclick=connectTerminal(event,1) onkeypress="return false" onkeydown="return false" disabled="disabled" /></span> <span id="disconnectbutton2span" style="margin-left:3px"><input type="button" id="disconnectbutton2" value="Disconnect" style="height:28px" onclick=connectTerminal(event,0) onkeypress="return false" onkeydown="return false" /></span> <span id="termstatus" style="line-height:22px">Disconnected</span><span id="termtitle"></span> </div> </div> </div> <div id="termarea3" style="width:100%;height: calc(100% - 60px);" cellpadding=0 cellspacing=0> <div id="termarea3x" style="width:100%;height:100%"> <div style="width:100%;height:100%" id="termarea3xdiv"></div> </div> </div> <div id="termarea4" style="position:relative;height:32px;"> <div class="areaFoot"> <div class="toright2"></div> <div style="height:28px"> <input id="termActionsBtn" style="margin-left:3px;height:28px" type=button title="Perform power actions on the device" onkeypress="return false" onkeydown="return false" value=Actions onclick=deviceActionFunction() /> </div> </div> </div> <div id=p12TermConsoleMsg style="display:none;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> </div> </div> <div id=p10files style="position:absolute;top:55px;bottom:0px;width:100%;display:none"> <table id="p13toolbar" style="width:100%;height:111px" cellpadding="0" cellspacing="0"> <tr> <td style="background-color:#C0C0C0;border-bottom:2px solid black;padding:2px;line-height:24px"> <div style="float:right;text-align:right"> <input id="filesActionsBtn" type=button onkeypress="return false" onkeydown="return false" value=Actions onclick=deviceActionFunction() style=margin-right:2px /> <div id="filesCustomUpperRight" style="float:left;margin-right:6px"></div> </div> <div style="margin-left:2px"> <input id=p13AutoConnect value="AutoConnect" onclick=autoConnectFiles(event) onkeypress="return false" onkeydown="return false" type="button" style="display:none"> <input id=p13Connect value="Connect" onclick=connectFiles(event) onkeypress="return false" onkeydown="return false" type="button"> <span class=textOverGray id=p13Status>Disconnected</span> </div> </td> </tr> <tr> <td style="width:100%;background-color:#d3d9d6;text-align:left;padding:4px" valign=bottom> <div style="width:100%;text-align:center"> <input type=button style="width:calc(100%/5 - 5px)" id=p13FolderUp disabled="disabled" onclick="p13folderup()" value="Up" /> <input type=button style="width:calc(100%/5 - 5px)" id=p13SelectAllButton disabled="disabled" onclick="p13selectallfile()" value="SelectAll" onkeypress="return false" onkeydown="return false" /> <input type=button style="width:calc(100%/5 - 5px)" id=p13RenameFileButton disabled="disabled" value="Rename" onclick="p13renamefile()" onkeypress="return false" onkeydown="return false" /> <input type=button style="width:calc(100%/5 - 5px)" id=p13DeleteFileButton disabled="disabled" value="Delete" onclick="p13deletefile()" onkeypress="return false" onkeydown="return false" /> <input type=button style="width:calc(100%/5 - 5px)" id=p13NewFolderButton disabled="disabled" value="Folder" onclick="p13createfolder()" onkeypress="return false" onkeydown="return false" /> </div> <div style="width:100%;text-align:center"> <input type=button style="width:calc(100%/5 - 5px)" id=p13UploadButton disabled="disabled" value="Upload" onclick="p13uploadFile()" onkeypress="return false" onkeydown="return false" /> <input type=button style="width:calc(100%/5 - 5px)" id=p13CutButton disabled="disabled" value="Cut" onclick="p13copyFile(1)" onkeypress="return false" onkeydown="return false" /> <input type=button style="width:calc(100%/5 - 5px)" id=p13CopyButton disabled="disabled" value="Copy" onclick="p13copyFile(0)" onkeypress="return false" onkeydown="return false" /> <input type=button style="width:calc(100%/5 - 5px)" id=p13PasteButton disabled="disabled" value="Paste" onclick="p13pasteFile()" onkeypress="return false" onkeydown="return false" /> <input type=button style="width:calc(100%/5 - 5px)" id=p13RefreshButton disabled="disabled" value="Refresh" onclick="p13folderup(9999)" onkeypress="return false" onkeydown="return false" /> </div> </td> </tr> <tr> <td style="background-color:#E4E9E7;height:28px"> <table style="width:100%"> <tr> <td id=p13currentpath style="overflow:hidden;padding-left:4px;padding-top:2px;color:black"></td> <td style="text-align:right;padding-right:4px"> <select id=p13sortdropdown onchange=p13updateFiles()> <option value=1 selected="selected">Sort by name</option> <option value=2>Sort by size</option> <option value=3>Sort by date</option> <option value=4>Descend by name</option> <option value=5>Descend by size</option> <option value=6>Descend by date</option> </select> </td> </tr> </table> </td> </tr> </table> <div id=p13FilesConsoleMsg style="display:none;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> <div id="p13filetable" style="width:100%;height:calc(100% - 133px);overflow:auto;-webkit-user-select:none"> <!-- <div id="p13bigok" style="width:256px;overflow:hidden;position:absolute;left:337px;top:200px;text-align:center;font-size:1600%;color:#AAAAAA;display:none"><b>✓</b></div> <div id="p13bigfail" style="width:256px;overflow:hidden;position:absolute;left:337px;top:200px;text-align:center;font-size:1600%;color:#AAAAAA;display:none"><b>✗</b></div> --> <span id="p13files"></span> </div> <table id="p13toolbarBottom" style="width:100%;height:22px;position:absolute;bottom:0px" cellpadding=0 cellspacing=0> <tr><td style="text-align:left;padding:3px;text-align:center;background-color:#D3D9D6;color:black"> <span id="p13bottomstatus"></span></td></tr> </table> </div> <div id=p10details style="overflow-y:scroll;position:absolute;top:55px;bottom:0px;width:100%"> <div id=p10detailshtml style="margin-left:-3px"></div> </div> <div id=p10console style="overflow:hidden;position:absolute;top:55px;bottom:0px;width:100%"> <table id="consoleTable" cellpadding=0 cellspacing=0> <tr style="height:28px"> <td class="areaHead"> <div class="toright2"> <div id=p15coreName></div> <input type=button id=p15uploadCore value="Agent Action" onclick=p15uploadCore(event) /> </div> <div id="p15statetext"></div> </td> </tr> <tr> <td id=p15agentConsole style="position:relative"> <pre id=p15agentConsoleText></pre> </td> </tr> <tr style="height:28px"> <td class="areaFoot"> <table style="width:100%"> <tr> <td style="width:99%"> <input id=p15consoleText style=width:100%;box-sizing:border-box onkeyup=p15consoleSend(event) /> </td> <td id="p15outputselecttd"> <select id=p15outputselect onchange="setupConsole()"> <option id="p15outputselect1" value=1>Agent</option> <option id="p15outputselect3" value=3>Push</option> <option id="p15outputselect2" value=2>MQTT</option> </select> </td> <td style="width:1%"><input id="id_p15consoleClear" type="button" class="bottombutton" value="Clear" onclick="p15consoleClear()" /></td> </tr> </table> </td> </tr> </table> </div> </div> <div id=p20 style="display:none;position:absolute;bottom:0;top:0;width:100%"> <table cellspacing=0 style="margin:0;padding:0;border-spacing:0;border:0;position:absolute;top:0"> <tr style=padding:0> <td style="padding:0;color:#c8c8c8;text-align:center;cursor:pointer" width=60px valign=top onclick=goBack()> <div style="padding:0;background-color:#036;width:10px;height:10px;float:right;border:0"> <div class="menucurve"></div> </div> <div style="padding:0;font-size:25px;background-color:#036;width:50px;border-radius:0 0 10px 0;height:36px">◀</div> </td> <td onclick="p20editmesh(1)"> <img src="/images/meshicon50.png" width=50 height=50 /> </td> <td onclick="p20editmesh(1)"> <div style=margin-left:5px> <strong style="font-size:large"><span id=p20meshName></span></strong><br /> </div> </td> </tr> </table> <div style="overflow-y:auto;position:absolute;top:55px;bottom:0px;left:0px;right:0px"> <div id=p20info style="margin-left:8px;margin-right:8px"></div> </div> </div> </div> </div> <div id=footer style="height:32px;width:100%;text-align:center;background-color:#113962;position:absolute;bottom:0px"> <table id=footerMenu cellpadding=0 cellspacing=0 style="height:32px;width:100%;color:white;cursor:pointer;table-layout:fixed"></table> </div> </div> <div id=dialog style="display:none"> <div style="width:100%;background-color:#003366;color:#FFF;border-radius:5px 5px 0 0"> <div id=id_dialogclose style=float:right;padding:5px;cursor:pointer onclick=setDialogMode()><b>X</b></div> <div id=id_dialogtitle style=padding:5px></div> <div style=width:100%;margin:6px></div> </div> <div style="margin-right:16px;margin-left:8px"> <div id=dialog1 style="margin:auto;text-align:center;margin:3px"> <div id=id_dialogMessage style="padding:10px"></div> </div> <div id=dialog2 style="margin:auto;margin:3px"> <div id=id_dialogOptions></div> </div> <div id=dialog3 style="margin:auto;margin:3px"> <select id="deskkeys" style="width:100%"> <option value=10>Ctrl+Alt+Del</option> <option value=11>Tab</option> <option value=5>Win</option> <option value=0>Win+Down</option> <option value=1>Win+Up</option> <option value=2>Win+L</option> <option value=3>Win+M</option> <option value=4>Shift+Win+M</option> <option value=6>Win+R</option> <option value=7>Alt-F4</option> <option value=8>Ctrl-W</option> <option value=9>Alt-Tab</option> <option value=12>Shift-F10</option> </select> </div> <div id=dialog7 style="margin:auto;margin:3px"> <div id="d7meshkvm"> <h4 style="width:100%;border-bottom:1px solid gray">Agent Remote Desktop</h4> <table style="width:100%"> <tr> <td> Quality </td> <td style="width:100px"> <select id="d7bitmapquality" style="float:right;width:200px" dir="rtl"></select> </td> </tr> <tr> <td> Scaling </td> <td style="width:100px"> <select id="d7bitmapscaling" style="float:right;width:200px" dir="rtl"> <option selected=selected value=1024>100%</option> <option value=896>87.5%</option> <option value=768>75%</option> <option value=640>62.5%</option> <option value=512>50%</option> <option value=384>37.5%</option> <option value=256>25%</option> <option value=128>12.5%</option> </select> </td> </tr> <tr> <td> Rate </td> <td style="width:100px"> <select id="d7framelimiter" style="float:right;width:200px" dir="rtl"> <option selected=selected value=50>Fast</option> <option value=100>Medium</option> <option value=400>Slow</option> <option value=1000>Very slow</option> </select> </td> </tr> <tr> <td></td> <td> <label style="display:block" id="d7deskAutoLockLabel"><input type="checkbox" id="d7deskAutoLock" />Lock on Disconnect</label> </td> </tr> </table> </div> <div id="d7amtkvm"> <h4 style="width:100%;border-bottom:1px solid gray">Intel® AMT Hardware KVM</h4> <table style="width:100%"> <tr> <td>Encoding</td> <td style="width:100px"> <select id="d7desktopmode" style="float:right;width:200px"> <option value="1">RLE8, Fastest</option> <option value="2">RLE16, Recommended</option> <option value="3">RAW8, Slow</option> <option value="4">RAW16, Very Slow</option> </select> </td> </tr> </table> </div> </div> </div> <div id="idx_dlgButtonBar" style="padding:10px;margin-bottom:20px"> <input id="idx_dlgCancelButton" type="button" value="Cancel" style="float:right;width:80px;margin-left:5px" onclick="dialogclose(0)"> <input id="idx_dlgOkButton" type="button" value="OK" style="float:right;width:80px" onclick="dialogclose(1)"> <div><input id="idx_dlgDeleteButton" type="button" value="Delete" style="display:none" onclick="dialogclose(2)" /></div> </div> </div> <div id=topMenu style="z-index:1000;background-color:#EEE;box-shadow:0px 0px 15px #666;font-family:Arial,Helvetica,sans-serif;border-radius:0px 0px 5px 5px;position:fixed;top:50px;right:5px;width:170px;display:none"> <div style="padding:12px;border-top:1px solid gray;color:black;cursor:pointer" onclick=topMenu(2)>My Files</div> <div style="padding:12px;border-top:1px solid gray;color:black;cursor:pointer" onclick=topMenu(1)>My Account</div> <div id="logoutMenuOption"><a href=/logout><div style="padding:12px;border-top:1px solid gray;color:black;cursor:pointer">Logout</div></a></div> </div> <audio id="chimes"><source src="sounds/chimes.mp3" type="audio/mp3" /></audio> <iframe name="fileUploadFrame" style=display:none></iframe> <script> 'use strict'; // Process server-side web state var webState = '{{{webstate}}}'; if (webState != '') { webState = JSON.parse(decodeURIComponent(webState)); } for (var i in webState) { localStorage.setItem(i, webState[i]); } if (webState && !webState.loctag) { delete localStorage.removeItem('loctag'); } // Fetch URL arguments & do sanitation var urlargs = parseUriArgs(); if (urlargs.key != null) { urlargs.key = "" + urlargs.key; } if (urlargs.key && (isAlphaNumeric(urlargs.key) == false)) { delete urlargs.key; } if (urlargs.locale && (isAlphaNumeric(urlargs.locale) == false)) { delete urlargs.locale; } delete urlargs.user; delete urlargs.pass; delete urlargs.viewmode; delete urlargs.gotonode; delete urlargs.gotomesh; delete urlargs.panel; // Check if we are in debug mode var args = parseUriArgs(); if (args.key && (isAlphaNumeric(args.key) == false)) { delete args.key; } if (args.locale && (isAlphaNumeric(args.locale) == false)) { delete args.locale; } var debugLevel = parseInt('{{{debuglevel}}}'); var features = parseInt('{{{features}}}'); var features2 = parseInt('{{{features2}}}'); var sessionTime = parseInt('{{{sessiontime}}}'); var sessionRefreshTimer = null; var domain = '{{{domain}}}'; var domainUrl = '{{{domainurl}}}'; var authCookie = '{{{authCookie}}}'; var authRelayCookie = '{{{authRelayCookie}}}'; var authCookieRenewTimer = null; var meshserver = null; var xdr = null; var usergroups = null; var stars = {}; // Devices that have been "stared" by the user. var serverinfo = null; var nodes = []; var meshes = {}; var filetree = {}; var userinfo = null; var serverinfo = null; var users = null; var nodeShortIdent = 0; var serverPublicNamePort = '{{{serverDnsName}}}:{{{serverPublicPort}}}'; var debugmode = false; var attemptWebRTC = ((features & 128) != 0); var StatusStrs = ["Disconnected", "Connecting...", "Setup...", "Connected", "Intel® AMT Connected"]; var agentsStr = ["Unknown", "Windows 32bit console", "Windows 64bit console", "Windows 32bit service", "Windows 64bit service", "Linux 32bit", "Linux 64bit", "MIPS", "XENx86", "Android ARM", "Linux ARM", "macOS x86-32bit", "Android x86", "PogoPlug ARM", "Android", "Linux Poky x86-32bit", "macOS x86-64bit", "ChromeOS", "Linux Poky x86-64bit", "Linux NoKVM x86-32bit", "Linux NoKVM x86-64bit", "Windows MinCore console", "Windows MinCore service", "NodeJS", "ARM-Linaro", "ARMv6l / ARMv7l", "ARMv8 64bit", "ARMv6l / ARMv7l / NoKVM", "MIPS24KC (OpenWRT)", "Apple Silicon", "FreeBSD x86-64", "Unknown", "Linux ARM 64 bit (glibc/2.24 NOKVM)", "Alpine Linux x86 64 Bit (MUSL)", "Assistant (Windows)", "Armada370 - ARM32/HF (libc/2.26)", "OpenWRT x86-64", "OpenBSD x86-64", "Unknown", "Unknown", "MIPSEL24KC (OpenWRT)", "ARMADA/CORTEX-A53/MUSL (OpenWRT)"]; var files; var terminal; var passRequirements = '{{{passRequirements}}}'; if (passRequirements != '') { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); } var sessionActivity = Date.now(); var deskPinchZoom; var deskKeyboardShortcuts = []; var nightMode = setNightMode(); var xterm = null; var xtermfit = null; var xtermResizeTimer = null; // Console Message Display Timers var p11DeskConsoleMsgTimer = null; var p12TermConsoleMsgTimer = null; var p13FilesConsoleMsgTimer = null; // Check if WebP is supported var webpSupport = false; check_webp_feature('lossy', function (f, x) { webpSupport = x; }); function startup() { if ((features & 32) == 0) { // Guard against other site's top frames (web bugs). var loc = null; try { loc = top.location.toString().toLowerCase(); } catch (e) { } if (top != self && (loc == null || top.active == false)) { top.location = self.location; return; } } if (!args.locale) { var x = getstore('loctag', 0); if ((x != null) && (x != '*')) { args.locale = x; } } window.onresize = center; center(); QV('changeEmailId', (features & 0x200000) == 0); QH('p1message', "Connecting..."); go(1); // Document keys document.onkeypress = ondeskkeypress; document.onkeydown = ondeskkeydown; document.onkeyup = ondeskkeyup; document.onclick = function (e) { if ((xxdialogMode == 999) && (e.target.id != 'topMenuIcon')) { QV('topMenu', false); xxdialogMode = 0; } } // Connect to the mesh server meshserver = MeshServerCreateControl(domainUrl, authCookie); meshserver.onStateChanged = onStateChanged; meshserver.onMessage = onMessage; meshserver.trace = args.trace; meshserver.Start(); // Setup stared devices try { stars = JSON.parse(getstore('stars', '{}')); } catch (ex) { } // Load desktop settings var t = localStorage.getItem('desktopsettings'); if (t != null) { desktopsettings = JSON.parse(t); } applyDesktopSettings(); // Arrange the user interface QV('manageEmail2FA', features & 0x00800000); QV('managePhoneNumber1', (features & 0x02000000) && (features & 0x04000000)); QV('managePhoneNumber2', (features & 0x02000000) && !(features & 0x04000000)); attemptWebRTC = 0; // For now, default WebRTC off unless we set it in the URL. if (args.webrtc != null) { attemptWebRTC = (args.webrtc == 1); } // Session Refresh Timer if (sessionTime >= 10) { sessionRefreshTimer = setTimeout(refreshCookieSession, Math.round((sessionTime * 60000) * 0.8)); } // Set the user's desktop shortcut keys deskKeyboardShortcuts = []; var deskKeyboardShortcutsStr = getstore('deskKeyShortcuts', '0x0A002E,0x100000,0x100028,0x100026,0x10004C,0x10004D,0x11004D,0x100052,0x020073,0x080057,0x020009,0x100025,0x100027').split(','); for (var i in deskKeyboardShortcutsStr) { deskKeyboardShortcuts.push(parseInt(deskKeyboardShortcutsStr[i])); } updateDeskShortcutKeys(); updateTermShortcutKeys(); } function refreshCookieSession() { var xdr = null; try { xdr = new XDomainRequest(); } catch (e) { } if (!xdr) xdr = new XMLHttpRequest(); xdr.open('GET', window.location.origin + domainUrl + 'refresh.ashx'); xdr.timeout = 15000; xdr.onload = function () { sessionRefreshTimer = setTimeout(refreshCookieSession, Math.round((sessionTime * 60000) * 0.8)); }; xdr.onerror = xdr.ontimeout = function () { sessionRefreshTimer = null; }; xdr.send(); } function onStateChanged(server, state, prevState, errorCode) { if (state == 0) { // Control web socket disconnected setDialogMode(0); // Close any dialog boxes if present go(0); // Go to disconnection panel deleteAllNotifications(); // Close and clear notifications if present if (errorCode == 'noauth') { QH('p0span', "Unable to perform authentication"); return; } if (prevState == 2) { setTimeout(serverPoll, 5000); } else { QH('p0span', "Unable to connect web socket"); } // Clean up here if (authCookieRenewTimer != null) { clearInterval(authCookieRenewTimer); authCookieRenewTimer = null; } } else if (state == 2) { // Fetch list of meshes, nodes, files meshserver.send({ action: 'usergroups' }); meshserver.send({ action: 'meshes' }); meshserver.send({ action: 'nodes' }); meshserver.send({ action: 'files' }); authCookieRenewTimer = setInterval(function () { meshserver.send({ action: 'authcookie' }); }, 1800000); // Request a cookie refresh every 30 minutes. } QV('topMenuIcon', state == 2); } // Poll the server, if it responds, refresh the page. function serverPoll() { xdr = null; try { xdr = new XDomainRequest(); } catch (e) { } if (!xdr) xdr = new XMLHttpRequest(); xdr.open('HEAD', window.location.href); xdr.timeout = 15000; // Make sure there isn't a reverse proxy in front that may just be returning 5xx codes // Status code 4xx should still be allowed, since a page could potentially be removed, etc xdr.onload = function () { if (xdr.status < 500) reload(); else setTimeout(serverPoll, 10000); }; xdr.onerror = xdr.ontimeout = function () { setTimeout(serverPoll, 10000); }; xdr.send(); } function updateSelf() { var accountSettingsLocked = ((features2 & 0x100) != 0); if (userinfo) { accountSettingsLocked = ((userinfo.siteadmin != 0xFFFFFFFF) && ((userinfo.siteadmin & 1024) != 0)) || ((features2 & 0x100) != 0); } // Not admin and have account features locked, or using a loginToken QV('p3AccountActions', ((features & 4) == 0) && (serverinfo.domainauth == false) && (accountSettingsLocked == false)); // Hide Account Actions if in single user mode or domain authentication QV('logoutMenuOption', ((features & 4) == 0) && (serverinfo.domainauth == false)); // Hide logout if in single user mode or domain authentication 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. QV('p2AccountImage', !accountSettingsLocked); QV('verifyEmailId', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true)); QV('manageAuthApp', features & 4096); QV('manageOtp', ((features & 4096) != 0) && ((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0))); QV('authPhoneNumberCheck', (userinfo.phone != null)); QV('authEmailSetupCheck', (userinfo.otpekey == 1) && (userinfo.email != null) && (userinfo.emailVerified == true)); QV('authAppSetupCheck', userinfo.otpsecret == 1); //QV('authKeySetupCheck', userinfo.otphkeys > 0); QV('authCodesSetupCheck', userinfo.otpkeys > 0); QV('p2AccountActions', ((features & 4) == 0) && (serverinfo.domainauth == false) && (userinfo != null) && (userinfo._id.split('/')[2].startsWith('~') == false)); // On the mobile app, don't allow group creation (for now). QV('p3createMeshLink1', false); QV('p3createMeshLink2', false); // Update user image if ((userinfo.flags != null) && (userinfo.flags & 1)) { if (userinfo.accountImageRnd == null) { userinfo.accountImageRnd = Math.floor(Math.random() * 9999999999); } Q('p2AccountImage').src = 'userimage.ashx?rnd=' + userinfo.accountImageRnd; } else { Q('p2AccountImage').src = 'images/user-256.png'; } if (typeof userinfo.passchange == 'number') { if (userinfo.passchange == -1) { QH('p2nextPasswordUpdateTime', " - Reset on next login."); } else if ((passRequirements != null) && (typeof passRequirements.reset == 'number')) { var seconds = (userinfo.passchange) + (passRequirements.reset * 86400) - Math.floor(Date.now() / 1000); if (seconds < 0) { QH('p2nextPasswordUpdateTime', " - Reset on next login."); } else if (seconds < 3600) { var secs = Math.floor(seconds / 60); QH('p2nextPasswordUpdateTime',format((secs == 1)?" - Reset in 1 minute.":" - Reset in {0} minutes.", secs)); } else if (seconds < 86400) { var hours = Math.floor(seconds / 3600); QH('p2nextPasswordUpdateTime', format((hours == 1) ? " - Reset in 1 hour." : " - Reset in {0} hours.", hours)); } else { var days = Math.floor(seconds / 86400); QH('p2nextPasswordUpdateTime', format((hours == 1) ? " - Reset in 1 day." : " - Reset in {0} days.", days)); } } } } function setSessionActivity() { sessionActivity = Date.now(); } function checkIdleSessionTimeout() { var delta = (Date.now() - sessionActivity); if (delta > serverinfo.timeout) { window.location.href = 'logout'; } } function onMessage(server, message) { switch (message.action) { case 'serverinfo': { serverinfo = message.serverinfo; if (serverinfo.timeout) { setInterval(checkIdleSessionTimeout, 10000); checkIdleSessionTimeout(); } if (userinfo != null) updateSelf(); if (serverinfo.certExpire != null) { var days = Math.floor((serverinfo.certExpire - Date.now()) / 86400000); if ((days >= 0) && (days < 20)) { addNotification({ text: format("Certificate expires in {0} day(s)", days) }); } } break; } case 'authcookie': { // Got an authentication cookie refresh authCookie = message.cookie; authRelayCookie = message.rcookie; break; } case 'userinfo': { userinfo = message.userinfo; QH('p3userName', userinfo.name); //updateSiteAdmin(); if (serverinfo != null) updateSelf(); break; } case 'users': { users = {}; for (var m in message.users) { users[message.users[m]._id] = message.users[m]; } if (currentUser != null) { currentUser = users[currentUser._id]; } updateUsers(); break; } case 'wssessioncount': { wssessions = message.wssessions; updateUsers(); break; } case 'meshes': { meshes = {}; for (var m in message.meshes) { meshes[message.meshes[m]._id] = message.meshes[m]; } if (currentMesh != null) { currentMesh = meshes[currentMesh._id]; } updateMeshes(); mainUpdate(4); break; } case 'usergroups': { var groupCount = 0; if (Array.isArray(message.ugroups)) { usergroups = {}; for (var i in message.ugroups) { groupCount++; usergroups[message.ugroups[i]._id] = message.ugroups[i]; } if (groupCount == 0) { usergroups = null; } } else { usergroups = message.ugroups; for (var i in message.ugroups) { groupCount++; } if (groupCount == 0) { usergroups = null; } } //mainUpdate(8192); break; } case 'files': { filetree = setupBackPointers(message.filetree); updateFiles(); //d3updatefiles(); break; } case 'nodes': { nodes = []; for (var m in message.nodes) { for (var n in message.nodes[m]) { message.nodes[m][n].namel = message.nodes[m][n].name.toLowerCase(); 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; } message.nodes[m][n].meshnamel = meshes[m]?meshes[m].name.toLowerCase():'*'; message.nodes[m][n].meshid = m; message.nodes[m][n].state = (message.nodes[m][n].state) ? (message.nodes[m][n].state) : 0; message.nodes[m][n].desc = message.nodes[m][n].desc; if (!message.nodes[m][n].icon) message.nodes[m][n].icon = 1; message.nodes[m][n].ident = ++nodeShortIdent; nodes.push(message.nodes[m][n]); } } // If we are currently looking at a node this is now gone, change the view. if ((currentNode != null) && (IsNodeViewable(currentNode) == false)) { currentNode = null; go(2); } // Change the reference to the current node if (currentNode != null) { currentNode = getNodeFromId(currentNode._id); if (currentNode != null) { gotoDevice(currentNode._id, xxcurrentView, true); } else { go(2); } } //onSortSelectChange(); //onSearchInputChanged(); mainUpdate(4); //refreshMap(false, true); if (xxcurrentView == 0) { if ('{{viewmode}}' != '') { go(parseInt('{{viewmode}}')); } else { setDialogMode(0); go(2); } } if ('{{currentNode}}' != '') { gotoDevice('{{currentNode}}', parseInt('{{viewmode}}')); } break; } case 'powertimeline': { if (message.nodeid != powerTimelineReq) break; powerTimelineNode = message.nodeid; powerTimeline = message.timeline; powerTimelineUpdate = Date.now() + 300000; // Update every 5 minutes for (var i in powerTimeline) { if (i % 2 == 1) { powerTimeline[i] = powerTimeline[i] * 1000; } } // Decompress time if (currentNode._id == message.nodeid) { drawDeviceTimeline(); } break; } case 'getsysinfo': { if (message.nodeid != powerTimelineReq) break; if (message.noinfo === true) { updateDeviceDetails(getNodeFromId(message.nodeid)); } else { updateDeviceDetails(getNodeFromId(message.nodeid), message.hardware); } break; } case 'lastconnect': { var node = getNodeFromId(message.nodeid); if (node != null) { node.lastconnect = message.time; node.lastaddr = message.addr; } break; } case 'msg': { // Check if this is a message from a node if (message.nodeid != null) { var index = -1; if (nodes != null) { for (var i in nodes) { if (nodes[i]._id == message.nodeid) { index = i; break; } } } if (index != -1) { if (message.type == 'console') { p15consoleReceive(nodes[index], message.value, message.source); } // This is a console message. else if (message.type == 'notify') { // This is a notification message. var n = getstore('notifications', 0); if (((n & 8) == 0) && (message.amtMessage != null)) { break; } // Intel AMT desktop & terminal messages should be ignored. var n = { text: message.value, title: message.title, icon: message.icon, titleid: message.titleid, msgid: message.msgid, args: message.args }; if (message.id != null) { n.id = message.id; } if (message.nodeid != null) { n.nodeid = message.nodeid; } if (message.tag != null) { n.tag = message.tag; } if (message.url != null) { n.url = message.url; } if (message.username != null) { n.username = message.username; } if (typeof message.maxtime == 'number') { n.maxtime = message.maxtime; } addNotification(n); } else if ((message.type == 'userSessions') && (currentNode != null) && (currentNode._id == message.nodeid) && (desktop == null)) { // Got list of user sessions var userSessions = []; if (message.data != null) { for (var i in message.data) { if ((message.data[i].State == 'Active') || (message.data[i].StationName == 'Console') || (debugmode == 3)) { userSessions.push(message.data[i]); } } } if (userSessions.length == 0) { connectDesktop(null, 1, null, message.tag); } // No active sessions, do a normal connection. else if (userSessions.length == 1) { connectDesktop(null, 1, userSessions[0].SessionId, message.tag); } // One active session, connect to it else { var x = ''; for (var i in userSessions) { 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; if (userSessions[i].Username) { if (userSessions[i].Domain) { x += ' - ' + userSessions[i].Domain + '/' + userSessions[i].Username; } else { x += ' - ' + userSessions[i].Username; } } x += '</div>'; } QH('p11DeskSessionSelector', x); QV('p11DeskSessionSelector', true); } } } } else { if (message.type == 'notify') { // This is a notification message. var n = { text: message.value, title: message.title, icon: message.icon, titleid: message.titleid, msgid: message.msgid, args: message.args }; if (message.id != null) { n.id = message.id; } if (message.tag != null) { n.tag = message.tag; } if (message.url != null) { n.url = message.url; } if (message.username != null) { n.username = message.username; } if (typeof message.maxtime == 'number') { n.maxtime = message.maxtime; } addNotification(n); } } break; } case 'getnetworkinfo': { if (currentNode._id != message.nodeid) return; updateDeviceDetails(getNodeFromId(message.nodeid), null, message); break; } case 'getNotes': { var n = Q('d2devNotes'); if (n && (message.id == decodeURIComponent(n.attributes['noteid'].value))) { if (message.notes) { QH('d2devNotes', decodeURIComponent(message.notes)); } else { QH('d2devNotes', ''); } var ro = (n.attributes['ro'].value == 'true'); if (ro == false) { // If we have permissions, set read/write on this note. n.removeAttribute('readonly'); QE('idx_dlgOkButton', true); QV('idx_dlgOkButton', true); focusTextBox('d2devNotes'); } } break; } case 'otpauth-request': { if ((xxdialogMode == 2) && (xxdialogTag == 'otpauth-request')) { var secret = message.secret; if (secret.length == 52) { secret = secret.split(/(.............)/).filter(Boolean).join(' '); } else if (secret.length == 32) { secret = secret.split(/(....)/).filter(Boolean).join(' '); secret = secret.substring(0, 20) + '<br/>' + secret.substring(20) } QH('d2optinfo', 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, use <a href=\"{0}\" rel=\"noreferrer noopener\" target=_blank> this link</a> or enter the secret below. Then, enter the current 6 digit token to activate 2-Step login.", message.url) + '<br /><br /><div style=width:100%;text-align:center><tt id=d2optsecret secret="' + message.secret + '" style=font-size:15px>' + secret + '</tt><br /><br />Token: <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></div>'); QV('idx_dlgOkButton', true); QE('idx_dlgOkButton', false); Q('d2otpauthinput').focus(); } break; } case 'otpauth-setup': { if (xxdialogMode) return; setDialogMode(2, "Authenticator App", 1, null, message.success ? "<b style=color:green>2-step login 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."); break; } case 'otpauth-clear': { if (xxdialogMode) return; setDialogMode(2, "Authenticator App", 1, null, message.success ? "<b style=color:green>2-step login activation removed</b>. You can reactivate this feature at any time." : "<b style=color:red>2-step login activation removal failed</b>. Try again."); break; } case 'otpauth-getpasswords': { if (xxdialogMode) return; var x = "One time tokens can be used as secondary authentication. Generate a set, print them and keep them in a safe place."; 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 style=width:100%;text-align:center>'; if (message.passwords) { var j = 0; for (var i in message.passwords) { if (++j % 2) { x += '<tr>'; } var p = '' + message.passwords[i].p; while (p.length < 8) { p = '0' + p; } if (message.passwords[i].u === true) { x += '<td>' + p.substring(0, 4) + ' ' + p.substring(4); } else { x += '<td><strike style=color:#BBB>' + p.substring(0, 4) + ' ' + p.substring(4); + '</strike>'; } } } else { x += '<tr><td>' + "No Active Tokens"; } x += '</table></div></div><br />'; x += '<div><input type=button value=\'' + "Close" + '\' onclick=setDialogMode(0) style=float:right></input>'; x += '<input type=button value=\'' + "New Tokens" + '\' onclick=\'account_manageOtp(1);\'></input>'; if (message.passwords != null) { x += '<input type=button value=\'' + "Clear" + '\' onclick=\'account_manageOtp(2);\'></input>'; } x += '</div><br />'; setDialogMode(2, "Manage Backup Codes", 8, null, x, 'otpauth-manage'); break; } case 'verifyPhone': { if (xxdialogMode && (xxdialogTag != 'verifyPhone')) return; var x = '<table><tr><td><img src="images/phone80.png" style=padding:8px>'; x += '<td>Check your phone and enter the verification code.'; 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>'; setDialogMode(2, "Phone Notifications", 3, account_managePhoneConfirm, x, message.cookie); Q('d2phoneCodeInput').focus(); account_managePhoneCodeValidate(); break; } case 'previousLogins': { if ((xxdialogMode == 2) && (xxdialogTag == 'previousLogins')) { var x = '', c = 'BBB', xx = ''; if (message.events.length == 0) { x += 'No previous login.'; } else { x += '<div style=max-height:260px;overflow-y:scroll;overflow-x:hidden>'; for (var i in message.events) { var m = message.events[i].m; if (m == 107) { m = "Valid login"; c = 'BBD1BB'; xx = ''; } else if (m == 108) { m = "Invalid 2FA"; c = 'DD9DC3'; xx = 'x'; } else if (m == 109) { m = "Locked account"; c = 'E1BBBB'; xx = 'x'; } else if (m == 110) { m = "Invalid password"; c = 'E1BBBB'; xx = 'x'; } x += '<div style=width:260px;background-color:#' + c + ';border-radius:6px;margin-bottom:4px;padding:4px><div><b>' + EscapeHtml(m) + '</b><br />' + printDateTime(new Date(message.events[i].t)) + '</div><div style=font-size:x-small>' + EscapeHtml(message.events[i].a.join(', ')) + '</div></div></tr>'; } x += '</div>'; } setDialogMode(2, "Previous Logins", 1, null, x); } break; } case 'event': { /* if (!message.event.nolog) { events.unshift(message.event); var eventLimit = parseInt(p3limitdropdown.value); while (events.length > eventLimit) { events.pop(); } // Remove element(s) at the end events_update(); } */ if (message.event.noact) break; // Take no action on this event switch (message.event.action) { case 'userWebState': { // New user web state, update the web page as needed if (localStorage != null) { var webstate = JSON.parse(message.event.state); for (var i in webstate) { localStorage.setItem(i, webstate[i]); } // Update stars if (webstate.stars != null) { stars = JSON.parse(webstate.stars); } // Update the web page if ((webstate.loctag != null) && (webstate.loctag != oldLoctag)) { if (webstate.loctag != null) { args.locale = webstate.loctag; } else { delete args.locale; } mainUpdate(4 + 128); } else if (webstate.stars != null) { mainUpdate(4); if (Q('SearchInput').value == '*') { onSearchInputChanged(); } } if (currentNode) { refreshDevice(currentNode._id); } // Set the user's desktop shortcut keys if (webstate.deskKeyShortcuts != null) { deskKeyboardShortcuts = []; var deskKeyboardShortcutsStr = webstate.deskKeyShortcuts.split(','); for (var i in deskKeyboardShortcutsStr) { deskKeyboardShortcuts.push(parseInt(deskKeyboardShortcutsStr[i])); } updateDeskShortcutKeys(); } } break; } case 'accountchange': { // An account was created or changed if ((typeof message.event.account != 'object') || (message.event.account == null)) { console.log(message.event); return; }; if (userinfo.name == message.event.account.name) { var newsiteadmin = message.event.account.siteadmin ? message.event.account.siteadmin : 0; var oldsiteadmin = userinfo.siteadmin ? userinfo.siteadmin : 0; if ((message.event.account.quota != userinfo.quota) || (((userinfo.siteadmin & 8) == 0) && ((message.event.account.siteadmin & 8) != 0))) { meshserver.send({ action: 'files' }); } userinfo = message.event.account; //if (oldsiteadmin != newsiteadmin) updateSiteAdmin(); updateSelf(); // If our list of nodes may have changes, request the new list now. if (message.event.nodeListChange == userinfo._id) { meshserver.send({ action: 'nodes' }); } } break; } case 'createusergroup': case 'usergroupchange': { // User group changed if (usergroups == null) { usergroups = {}; } var ugroup = usergroups[message.event.ugrpid]; if (ugroup == null) { // This is a new user group for us usergroups[message.event.ugrpid] = { _id: message.event.ugrpid, name: message.event.name, desc: message.event.desc, domain: message.event.domain, links: message.event.links }; } else { // This is an existing user group ugroup.name = message.event.name; ugroup.desc = message.event.desc; ugroup.links = message.event.links; ugroup.flags = message.event.flags; } //mainUpdate(8192 + 16384); // Group update, refresh all our device groups and nodes. TODO: Optimize this to only do this when needed. meshserver.send({ action: 'meshes' }); meshserver.send({ action: 'nodes' }); break; } case 'deleteusergroup': { // User group removed if ((usergroups != null) && (usergroups[message.event.ugrpid] != null)) { delete usergroups[message.event.ugrpid]; var c = 0; for (var i in usergroups) { c++; } if (c == 0) { usergroups = null; } // If user groups is empty, set it to null. //mainUpdate(8192 + 16384); } break; } case 'createmesh': { // A new mesh was created if ((meshes[message.event.meshid] == null) && ((userinfo.manageAllDeviceGroups) || (message.event.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. meshes[message.event.meshid] = { _id: message.event.meshid, name: message.event.name, mtype: message.event.mtype, desc: message.event.desc, links: message.event.links }; mainUpdate(4 + 128); meshserver.send({ action: 'files' }); } break; } case 'meshchange': { // Update mesh information if (meshes[message.event.meshid] == null) { // Check if we have any access to this device group var add = false; if (message.event.links[userinfo._id] != null) { add = true; } if (userinfo.links[message.event.meshid] != null) { add = true; } for (var i in userinfo.links) { if ((i.startsWith('ugrp/')) && (message.event.links[i] != null)) { add = true; } } // This is a new mesh for us if (add) { meshes[message.event.meshid] = { _id: message.event.meshid, name: message.event.name, mtype: message.event.mtype, desc: message.event.desc, links: message.event.links }; meshserver.send({ action: 'nodes' }); // Request a refresh of all nodes (TODO: We could optimize this to only request nodes for the new mesh). } } else { // This is an existing mesh if (meshes[message.event.meshid].name != message.event.name) { meshes[message.event.meshid].name = message.event.name; for (var i in nodes) { if (nodes[i].meshid == message.event.meshid) { nodes[i].meshnamel = message.event.name.toLowerCase(); } } } meshes[message.event.meshid].desc = message.event.desc; meshes[message.event.meshid].links = message.event.links; // Check if we lost rights to this mesh in this change. if (IsMeshViewable(message.event.meshid) == false) { if ((xxcurrentView == 20) && (currentMesh == meshes[message.event.meshid])) go(2); delete meshes[message.event.meshid]; // Delete all nodes in that mesh, except ones with direct links var newnodes = []; 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]); } } nodes = newnodes; // If we are looking at a node in the deleted mesh, move back to "My Devices" if (xxcurrentView >= 10 && xxcurrentView < 20 && currentNode && !IsNodeViewable(currentNode)) { setDialogMode(0); go(2); currentNode = null; } } } mainUpdate(4 + 128); meshserver.send({ action: 'files' }); // If we are looking at a mesh that is now deleted, move back to "My Account" if (xxcurrentView == 20 && currentMesh._id == message.event.meshid) { p20updateMesh(); } break; } case 'deletemesh': { // Delete the mesh if (meshes[message.event.meshid]) { delete meshes[message.event.meshid]; updateMeshes(); meshserver.send({ action: 'files' }); } // Delete all nodes in that mesh var newnodes = []; for (var i in nodes) { if (nodes[i].meshid != message.event.meshid) { newnodes.push(nodes[i]); } } nodes = newnodes; mainUpdate(4); // If we are looking at a mesh that is now deleted, move back to "My Account" if (xxcurrentView >= 20 && xxcurrentView < 30 && currentMesh._id == message.event.meshid) { setDialogMode(0); go(2); } // If we are looking at a node in the deleted mesh, move back to "My Devices" if (xxcurrentView >= 10 && xxcurrentView < 20 && currentNode && !IsNodeViewable(currentNode)) { setDialogMode(0); go(2); } break; } case 'addnode': { var node = message.event.node; 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. if (getNodeFromId(node._id) != null) break; // This node is already known. node.namel = node.name.toLowerCase(); if (node.rname) { node.rnamel = node.rname.toLowerCase(); } else { node.rnamel = node.namel; } node.meshnamel = meshes[node.meshid]?meshes[node.meshid].name.toLowerCase():'*'; node.state = 0; if (!node.icon) node.icon = 1; node.ident = ++nodeShortIdent; nodes.push(node); //onSortSelectChange(); //onSearchInputChanged(); mainUpdate(4); //updateMapMarkers(); break; } case 'removenode': { var index = -1; for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } } if (index != -1) { var node = nodes[index]; if (currentNode == node) { if (xxcurrentView >= 10 && xxcurrentView < 20) { setDialogMode(0); go(2); } currentNode = null; // TODO: Correctly disconnect from this node (Desktop/Terminal/Files...) } nodes.splice(index, 1); mainUpdate(4); //updateMapMarkers(); } break; } case 'changenode': { var index = -1; for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } } if (index != -1) { var node = nodes[index]; // Change the node node.name = message.event.node.name; node.rname = message.event.node.rname; node.host = message.event.node.host; node.desc = message.event.node.desc; node.publicip = message.event.node.publicip; node.iploc = message.event.node.iploc; node.wifiloc = message.event.node.wifiloc; node.gpsloc = message.event.node.gpsloc; node.tags = message.event.node.tags; node.ssh = message.event.node.ssh; node.rdp = message.event.node.rdp; node.userloc = message.event.node.userloc; node.rdpport = message.event.node.rdpport; node.rfbport = message.event.node.rfbport; node.sshport = message.event.node.sshport; node.consent = message.event.node.consent; node.pmt = message.event.node.pmt; if (message.event.node.agent != null) { if (node.agent == null) node.agent = {}; if (message.event.node.agent.ver != null) { node.agent.ver = message.event.node.agent.ver; } if (message.event.node.agent.id != null) { node.agent.id = message.event.node.agent.id; } if (message.event.node.agent.caps != null) { node.agent.caps = message.event.node.agent.caps; } if (message.event.node.agent.root != null) { node.agent.root = message.event.node.agent.root; } if (message.event.node.agent.core != null) { node.agent.core = message.event.node.agent.core; } else { if (node.agent.core) { delete node.agent.core; } } node.agent.tag = message.event.node.agent.tag; } if (message.event.node.intelamt != null) { if (node.intelamt == null) node.intelamt = {}; if (message.event.node.intelamt.state != null) { node.intelamt.state = message.event.node.intelamt.state; } if (message.event.node.intelamt.host != null) { node.intelamt.user = message.event.node.intelamt.host; } if (message.event.node.intelamt.user != null) { node.intelamt.user = message.event.node.intelamt.user; } if (message.event.node.intelamt.tls != null) { node.intelamt.tls = message.event.node.intelamt.tls; } if (message.event.node.intelamt.ver != null) { node.intelamt.ver = message.event.node.intelamt.ver; } if (message.event.node.intelamt.tag != null) { node.intelamt.tag = message.event.node.intelamt.tag; } if (message.event.node.intelamt.uuid != null) { node.intelamt.uuid = message.event.node.intelamt.uuid; } if (message.event.node.intelamt.realm != null) { node.intelamt.realm = message.event.node.intelamt.realm; } if (message.event.node.intelamt.flags != null) { node.intelamt.flags = message.event.node.intelamt.flags; } if (message.event.node.intelamt.warn != null) { node.intelamt.warn = message.event.node.intelamt.warn; } else { delete node.intelamt.warn; } } if (message.event.node.av != null) { node.av = message.event.node.av; } if (message.event.node.wsc != null) { node.wsc = message.event.node.wsc; } node.namel = node.name.toLowerCase(); if (node.rname) { node.rnamel = node.rname.toLowerCase(); } else { node.rnamel = node.namel; } if (message.event.node.icon) { node.icon = message.event.node.icon; } //onSortSelectChange(true); //drawNotifications(); refreshDevice(node._id); updateDeviceViewDevice(node); if (currentNode == node) { updateDeviceDetails(); } //if ((currentNode == node) && (xxdialogMode != null) && (xxdialogTag == '@xxmap')) { p10showNodeLocationDialog(); } } break; } case 'nodemeshchange': { var index = -1; for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } } if (index != -1) { var node = nodes[index]; if ((meshes[message.event.newMeshId] == null) && ((userinfo.links == null) || (userinfo.links[node._id] == null))) { // We don't see the new mesh, remove this device // TODO: Correctly disconnect from this node (Desktop/Terminal/Files...) if (xxcurrentView >= 10 && xxcurrentView < 20 && currentNode && !IsNodeViewable(currentNode)) { setDialogMode(0); go(2); currentNode = null; } nodes.splice(index, 1); } else { // We see the new mesh, move this device node.meshid = message.event.newMeshId; node.meshnamel = meshes[message.event.newMeshId]?meshes[message.event.newMeshId].name.toLowerCase():'*'; } mainUpdate(4); refreshDevice(message.event.nodeid); } else { // This is a new device, add it. var node = message.event.node; 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. node.namel = node.name.toLowerCase(); if (node.rname) { node.rnamel = node.rname.toLowerCase(); } else { node.rnamel = node.namel; } node.meshnamel = meshes[node.meshid]?meshes[node.meshid].name.toLowerCase():'*'; node.state = 0; if (!node.icon) node.icon = 1; node.ident = ++nodeShortIdent; if (nodes == null) { } nodes.push(node); // Web page update //mainUpdate(1 | 2 | 4 | 16); mainUpdate(4); } break; } case 'nodeconnect': { // Indicated a node has changed connectivity state var index = -1; for (var i in nodes) { if (nodes[i]._id == message.event.nodeid) { index = i; break; } } if (index != -1) { var node = nodes[index]; // Change the node connection state node.conn = message.event.conn; node.pwr = message.event.pwr; // Clear sesssion information if needed if ((node.conn & 1) == 0) { delete node.sessions; } refreshDevice(node._id); updateDeviceViewDevice(node); } break; } case 'login': { // Update the last login time if (users != null && users['user/' + domain + '/' + message.event.username.toLowerCase()]) { users['user/' + domain + '/' + message.event.username.toLowerCase()].login = message.event.time; } break; } case 'notify': { var n = { text: message.event.value, title: message.event.title, icon: message.event.icon, titleid: message.titleid, msgid: message.msgid, args: message.args }; if (message.id != null) { n.id = message.id; } if (message.event.tag != null) { n.tag = message.event.tag; } if (typeof message.maxtime == 'number') { n.maxtime = message.maxtime; } addNotification(n); break; } case 'sysinfohash': { // If the sysinfo document has changed and we are looking at it, request an update. if ((currentNode != null) && (message.event.nodeid == powerTimelineReq)) { meshserver.send({ action: 'getsysinfo', nodeid: message.event.nodeid }); } break; } case 'ifchange': { // Network interface changed for a device, if we are currently viewing this device, ask for an update. if ((currentNode != null) && (currentNode._id == message.event.nodeid)) { meshserver.send({ action: 'getnetworkinfo', nodeid: currentNode._id }); } break; } case 'devicesessions': { // List of sessions for a given device var node = getNodeFromId(message.event.nodeid); if (node == null) break; // Unknown node node.sessions = message.event.sessions; if (node.sessions != null) { for (var i in node.sessions) { if (Object.keys(node.sessions[i]).length == 0) { delete node.sessions[i]; } } if (Object.keys(node.sessions).length == 0) { delete node.sessions; } } refreshDevice(message.event.nodeid); updateDeviceViewDevice(node); //if ((currentNode != null) && (currentNode._id == message.event.nodeid)) { gotoDevice(currentNode._id, xxcurrentView, true); } // If we are looking at the sessions dialog box for this device now, update it if (xxdialogTag == ('SESSIONS-' + message.event.nodeid)) { showDeviceSessions(message.event.nodeid, true); } //if (xxdialogTag == ('MESSAGES-' + message.event.nodeid)) { showDeviceMessages(message.event.nodeid, true); } if (xxdialogTag == ('HELPREQ-' + message.event.nodeid)) { showDeviceHelpRequests(message.event.nodeid, true); } break; } case 'stopped': { // Server is stopping. // TODO: Disconnect break; } default: //console.log('Unknown message.event.action', message.event.action); break; } break; } default: //console.log('Unknown message.action', message.action); break; } } // To boost the speed of the web page when even floods occur, this method perform a delayed update on the web page. var updateNaggleTimer = null; var updateNaggleFlags = 0; function mainUpdate(flags) { updateNaggleFlags |= flags; if (updateNaggleTimer == null) { updateNaggleTimer = setTimeout(function () { if (updateNaggleFlags & 4) { updateDevices(); updateDeviceDetails(); } if (updateNaggleFlags & 128) { updateMeshes(); } updateNaggleTimer = null; updateNaggleFlags = 0; gotoStartViewPage(); }, 150); } } // Go to the correct starting view page function gotoStartViewPage() { var xviewmode = parseInt('{{viewmode}}'); if (xxcurrentView > 1) return; if ('{{currentNode}}'.toLowerCase() != '') { // The .toLowerCase here is the minifier will not optimize this out. if (getNodeFromId('{{currentNode}}') == null) return; // This node is not loaded yet gotoDevice('{{currentNode}}', xviewmode); } else if (args.gotonode != null) { 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 if (getNodeFromId('node/' + domain + '/' + args.gotonode) == null) return; // This node is not loaded yet if (args.panel) { currentDevicePanel = parseInt(args.panel); } gotoDevice('node/' + domain + '/' + args.gotonode, xviewmode); } else if (args.gotomesh != null) { if (meshes['mesh/' + domain + '/' + args.gotomesh] == null) return; // This device group is not loaded yet gotoMesh('mesh/' + domain + '/' + args.gotomesh); go(xviewmode); } else if (!isNaN(xviewmode)) { go(xviewmode); } else { setDialogMode(0); go(1); } delete args.gotonode; delete args.gotomesh; delete args.panel; if (xxcurrentView < 2) { go(2); } } // // Menu System // function topMenu(select) { if ((xxdialogMode != null) && (xxdialogMode != 0) && (xxdialogMode != 999)) return; if (select === undefined) { var x = (QS('topMenu').display == 'none'); if (x == true) { if ((xxdialogMode == 0) || (xxdialogMode == null)) { QV('topMenu', true); xxdialogMode = 999; } } else { QV('topMenu', false); xxdialogMode = 0; } } else { QV('topMenu', false); xxdialogMode = 0; if ((select == 1) && (xxcurrentView != 3)) { goForward('account'); } // My Account if ((select == 2) && (xxcurrentView != 5)) { goForward('files'); } // My Files } } var backStack = []; function goBack() { if (xxdialogMode) return; if (backStack.length > 0) { backStack.pop(); } goStack(); } function goForward(id) { if (xxdialogMode) return; backStack.push(id); goStack(); } function goStack() { if (backStack.length == 0) { go(2); return; } var id = backStack[backStack.length - 1], idtype = id.split('/')[0]; if (idtype == 'node') { setupDeviceMenu(0); gotoDevice(id); } if (idtype == 'mesh') { gotoMesh(id); } if (idtype == 'account') { go(3); } if (idtype == 'devices') { go(2); } if (idtype == 'files') { go(5); } } function updateFooterMenu(options) { while (options != null && options.length < 3) { options.push({ n: '' }); } var x = '', prev = ''; if (options != null) { for (var i in options) { x += '<td style="cursor:pointer' + ((prev == '') ? '' : ';border-left:solid 1px white') + '" onclick="' + options[i].f + '">' + options[i].n; prev = options[i].n; } } QH('footerMenu', '<tr>' + x); } // // MY ACCOUNT // function account_viewPreviousLogins() { if (xxdialogMode) return; setDialogMode(2, "Previous Logins", 1, null, "Loading...", 'previousLogins'); meshserver.send({ action: 'previousLogins' }); } function account_manageImage(mode) { if (xxdialogMode) return; var user = (mode == 0) ? userinfo : currentUser; var x = '<input id=p2file type=file style=width:100% accept="image/*" onchange=account_manageImageEx()><div style=width:100%><canvas id=p2canvas width=256 height=256 style="width:256px;height:256px;margin-left:12px;margin-top:8px;border-radius:16px;box-shadow: 0px 0px 15px #000" onclick=account_canvasClick() /></div>'; setDialogMode(2, "Manage Account Image", 7, account_manageImageEx2, x, user._id); var ctx = Q('p2canvas').getContext('2d'); if (user.accountImageRnd == null) { user.accountImageRnd = Math.floor(Math.random() * 9999999999); } var arg = ''; if (mode == 1) { arg = '&id=' + user._id.split('/')[2]; } var myImg = new Image(); myImg.onload = function () { ctx.clearRect(0, 0, 256, 256); ctx.drawImage(myImg, 0, 0); }; myImg.src = ((user.flags != null) && (user.flags & 1)) ? ('userimage.ashx?rnd=' + user.accountImageRnd + arg) : 'images/user-256.png'; QE('idx_dlgDeleteButton', (user.flags != null) && (user.flags & 1)); QE('idx_dlgOkButton', false); } function account_canvasClick() { Q('p2file').click(); } function account_manageImageEx() { var file = Q('p2file').files[0]; var img = new Image; img.onload = function () { var cx = 0, cy = 0, min = Math.min(img.width, img.height); if (img.width > min) { cx = (img.width - min) / 2; } if (img.height > min) { cy = (img.height - min) / 2; } var ctx = Q('p2canvas').getContext('2d'); ctx.imageSmoothingEnabled = true; ctx.webkitImageSmoothingEnabled = true; ctx.mozImageSmoothingEnabled = true; ctx.clearRect(0, 0, 256, 256); ctx.drawImage(img, cx, cy, min, min, 0, 0, 256, 256); QE('idx_dlgOkButton', true); } img.src = URL.createObjectURL(file); } function account_manageImageEx2(b, userid) { // Send updated image, or 0 if we pressed the delete button meshserver.send({ action: 'updateUserImage', userid: userid, image: (b == 2) ? 0 : Q('p2canvas').toDataURL('image/jpeg', 0.8) }); //meshserver.send({ action: 'updateUserImage', image: (b == 2)?0:Q('p2canvas').toDataURL('image/png', 0.8) }); } function toggleNightMode() { if (xxdialogMode) return; var cNightMode = getstore('nightMode', '0'); var x = '<input type=radio id=night0 name=nightmoderadio value=0 ' + ((cNightMode == 0) ? 'checked' : '') + '><label for=night0>' + "Browser default" + '</label><br>'; x += '<input type=radio id=night2 name=nightmoderadio value=2 ' + ((cNightMode == 2) ? 'checked' : '') + '><label for=night2>' + "Light mode" + '</label><br>'; x += '<input type=radio id=night1 name=nightmoderadio value=1 ' + ((cNightMode == 1) ? 'checked' : '') + '><label for=night1>' + "Dark mode" + '</label><br>'; setDialogMode(2, "Night Mode", 3, toggleNightModeEx, x); QV('uiMenu', false); } function toggleNightModeEx() { // Save new night mode var nNightMode = '0'; if (Q('night1').checked) { nNightMode = '1'; } if (Q('night2').checked) { nNightMode = '2'; } putstore('nightMode', nNightMode); setNightMode(); } function setNightMode() { // Set night mode var nNightMode = getstore('nightMode', '0') nightMode = false; if (nNightMode == '1') { nightMode = true; } else if ((nNightMode == '0') && (window.matchMedia)) { nightMode = window.matchMedia('(prefers-color-scheme: dark)').matches } if (nightMode) { QC('body').add('night'); QS('body')['background-color'] = '#000'; QS('body')['color'] = 'lightgray'; } else { QC('body').remove('night'); QS('body')['background-color'] = '#FFF'; QS('body')['color'] = 'black'; } return nightMode; } function account_managePhone() { if (xxdialogMode || ((features & 0x02000000) == 0)) return; var x; if (userinfo.phone != null) { x = '<table style=width:100%><tr><td style=width:56px><img src="images/phone80.png" style=padding:8px>'; x += '<td style=text-align:center><div style=padding:6px>' + "Verified phone number" + '</div><div style=font-size:20px>' + userinfo.phone + '</div>'; x += '<div style=margin:10px><label><input id=d2delPhone type=checkbox onclick=account_managePhoneRemoveValidate() />' + "Remove phone number" + '</label></div>'; setDialogMode(2, "Phone Notifications", 3, account_managePhoneRemove, x); account_managePhoneRemoveValidate(); } else { x = '<table style=width:100%><tr><td style=width:56px><img src="images/phone80.png" style=padding:8px>'; x += '<td>Enter your SMS capable phone number. Once verified, the number may be used for login verification and other notifications.'; x += '<br /><br /><div style=width:100%;text-align:center>' + "Phone number:" + ' <input type=tel pattern="[0-9]" autocomplete="tel" inputmode="tel" maxlength=18 id=d2phoneinput onKeyUp=account_managePhoneValidate() onkeypress="if (event.key==\'Enter\') account_managePhoneValidate(1)"></div></table>'; setDialogMode(2, "Phone Notifications", 3, account_managePhoneAdd, x, 'verifyPhone'); Q('d2phoneinput').focus(); account_managePhoneValidate(); } } function isPhoneNumber(x) { return x.match(/^\(?([0-9]{3,4})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/) } function account_managePhoneValidate(x) { var ok = isPhoneNumber(Q('d2phoneinput').value); QE('idx_dlgOkButton', ok); if ((x == 1) && ok) { dialogclose(1); } } function account_managePhoneCodeValidate(x) { var ok = (Q('d2phoneCodeInput').value.length == 6) && Q('d2phoneCodeInput').value.match(/[0-9]/); QE('idx_dlgOkButton', ok); if ((x == 1) && ok) { dialogclose(1); } } function account_managePhoneConfirm(b, tag) { meshserver.send({ action: 'confirmPhone', code: Q('d2phoneCodeInput').value, cookie: tag }); } function account_managePhoneAdd() { if (isPhoneNumber(Q('d2phoneinput').value) == false) return; QE('d2phoneinput', false); meshserver.send({ action: 'verifyPhone', phone: Q('d2phoneinput').value }); } function account_managePhoneRemove() { if (Q('d2delPhone').checked) { meshserver.send({ action: 'removePhone' }); } } function account_managePhoneRemoveValidate() { QE('idx_dlgOkButton', Q('d2delPhone').checked); } function account_manageAuthEmail() { if (xxdialogMode || ((features & 0x00800000) == 0)) return; var emailU2Fenabled = ((userinfo.otpekey == 1) && (userinfo.email != null) && (userinfo.emailVerified == true)); setDialogMode(2, "Email Authentication", 1, function () { if (emailU2Fenabled != Q('email2facheck').checked) { meshserver.send({ action: 'otpemail', enabled: Q('email2facheck').checked }); } }, "When enabled, on each login, you will be given the option to receive a login token to you email account for added security." + '<br /><br /><label><input id=email2facheck type=checkbox ' + (emailU2Fenabled ? 'checked' : '') + '/>' + "Enable email two-factor authenticaiton." + '</label>'); } function account_manageAuthApp() { if (xxdialogMode || ((features & 4096) == 0)) return; if (userinfo.otpsecret == 1) { account_removeOtp(); } else { account_addOtp(); } } function account_addOtp() { if (xxdialogMode || (userinfo.otpsecret == 1) || ((features & 4096) == 0)) return; setDialogMode(2, "Authenticator App", 2, function () { meshserver.send({ action: 'otpauth-setup', secret: Q('d2optsecret').attributes.secret.value, token: Q('d2otpauthinput').value }); }, '<div id=d2optinfo>' + "Loading..." + '</div>', 'otpauth-request'); meshserver.send({ action: 'otpauth-request' }); } function account_addOtpCheck(e) { var tokenIsValid = (Q('d2otpauthinput').value.length == 6); QE('idx_dlgOkButton', tokenIsValid); if (e && (e.keyCode == 13) && tokenIsValid) { dialogclose(1); } } function account_removeOtp() { if (xxdialogMode || (userinfo.otpsecret != 1) || ((features & 4096) == 0)) return; setDialogMode(2, "Authenticator App", 3, function () { meshserver.send({ action: 'otpauth-clear' }); }, "Confirm removal of authenticator application 2-step login?"); } function account_manageOtp(action) { if ((xxdialogMode == 2) && (xxdialogTag == 'otpauth-manage')) { dialogclose(0); } if (xxdialogMode || ((features & 4096) == 0) || ((userinfo.otpsecret != 1) && (userinfo.otphkeys < 1))) return; meshserver.send({ action: 'otpauth-getpasswords', subaction: action }); } function account_showVerifyEmail() { if (xxdialogMode || (userinfo.emailVerified == true) || (serverinfo.emailcheck != true)) return; var x = "Click ok to send a verification mail to:" + '<br /><div style=padding:8px><b>' + EscapeHtml(userinfo.email) + '</b></div>' + "Please wait a few minute to receive the verification."; setDialogMode(2, "Email Verification", 3, account_showVerifyEmailEx, x); } function account_showVerifyEmailEx() { meshserver.send({ action: 'verifyemail', email: userinfo.email }); } function account_showChangeEmail() { if (xxdialogMode) return; var x = addHtmlValue("Email", '<input id=dp3email style=width:170px maxlength=256 onchange=account_validateEmail() onkeyup=account_validateEmail(event) />'); setDialogMode(2, "Email Address Change", 3, account_changeEmail, x); if (userinfo.email != null) { Q('dp3email').value = userinfo.email; } account_validateEmail(); Q('dp3email').focus(); } function account_validateEmail(e, email) { QE('idx_dlgOkButton', validateEmail(Q('dp3email').value) && (Q('dp3email').value != userinfo.email)); if ((e != null) && (e.keyCode == 13)) { dialogclose(1); } } function account_changeEmail() { meshserver.send({ action: 'changeemail', email: Q('dp3email').value }); } function account_showDeleteAccount() { if (xxdialogMode) return; var x = '<form method=post><table style=margin-left:10px><input type=hidden name=action value=deleteaccount /><input type=hidden name=authcookie value=' + authCookie + ' /><tr>'; x += '<td align=right>' + "Password:" + '</td><td><input id=apassword1 type=password name=apassword1 autocomplete=off onchange=account_validateDeleteAccount() onkeyup=account_validateDeleteAccount() /></td>'; x += '</tr><tr><td align=right>' + "Password:" + '</td><td><input id=apassword2 type=password name=apassword2 autocomplete=off onchange=account_validateDeleteAccount() onkeyup=account_validateDeleteAccount() /></td>'; x += '</tr></table><div style=padding:10px;margin-bottom:4px>'; x += '<input id=account_dlgCancelButton type=button value="' + "Cancel" + '" style=float:right;width:80px;margin-left:5px onclick=dialogclose(0)>'; x += '<input id=account_dlgOkButton type=submit value="' + "OK" + '" style="float:right;width:80px" onclick=dialogclose(1)>'; x += '</div><br /></form>'; setDialogMode(2, "Delete Account", 0, null, x); account_validateDeleteAccount(); Q('apassword1').focus(); } function account_showChangePassword() { if (xxdialogMode) return false; var x = '<table style=margin-left:10px>'; x += '<tr><td align=right>' + nobreak("Old password:") + '</td><td><input id=apassword0 type=password name=apassword0 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /> <b></b></td></tr>'; x += '<tr><td align=right>' + nobreak("New password:") + '</td><td><input id=apassword1 type=password name=apassword1 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /> <b><span id=dxPassWarn></span></b></td></tr>'; x += '<tr><td align=right>' + nobreak("New password:") + '</td><td><input id=apassword2 type=password name=apassword2 autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /></td></tr>'; if (features & 0x00010000) { x += '<tr><td align=right>' + "Password hint:" + '</td><td><input id=apasswordhint name=apasswordhint maxlength=250 type=text autocomplete=off onchange=account_validateNewPassword() onkeyup=account_validateNewPassword() onkeydown=account_validateNewPassword() /></td></tr>'; } x += '</table>' if (passRequirements) { var r = [], rc = 0; for (var i in passRequirements) { if ((i != 'reset') && (i != 'hint')) { r.push(i + ':' + passRequirements[i]); rc++; } } if (rc > 0) { x += '<br /><span style=font-size:x-small>' + format("Requirements: {0}.", r.join(', ')) + '</span>'; } } x += '<br />'; setDialogMode(2, "Change Password", 3, account_showChangePasswordEx, x); Q('apassword0').focus(); account_validateNewPassword(); return false; } function account_showChangePasswordEx() { if (Q('apassword1').value == Q('apassword2').value) { var r = { action: 'changepassword', oldpass: Q('apassword0').value, newpass: Q('apassword1').value }; if (features & 0x00010000) { r.hint = Q('apasswordhint').value; } meshserver.send(r); } } function account_createMesh() { if (xxdialogMode) return; // Check if we are disallowed from creating a device group if ((userinfo.siteadmin != 0xFFFFFFFF) && ((userinfo.siteadmin & 64) != 0)) { setDialogMode(2, "New Device Group", 1, null, "This account does not have the rights to create a new device group."); return; } // Remind the user to verify the email address if ((userinfo.emailVerified !== true) && (serverinfo.emailcheck == true) && (userinfo.siteadmin != 0xFFFFFFFF)) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until a email address is verified. This is required for password recovery. Go to the \"My Account\" to change and verify an email address."); return; } // Remind the user to add two factor authentication if ((features & 0x00040000) && !((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0) || (userinfo.otpkeys > 0) || ((features & 0x00800000) && (userinfo.otpekey == 1)))) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until two-factor authentication is enabled. This is required for extra security. Go to the \"My Account\" and look at the \"Account Security\" section."); return; } // We are allowed, let's prompt to information var x = addHtmlValue("Name", '<input id=dp3meshname style=width:170px maxlength=64 onchange=account_validateMeshCreate() onkeyup=account_validateMeshCreate() />'); x += addHtmlValue("Type", '<div style=width:170px;margin:0;padding:0><select id=dp3meshtype style=width:100% onchange=account_validateMeshCreate() ><option value=2>' + "Software Agent Group" + '</option><option value=1>' + "Intel® AMT only" + '</option></select></div>'); x += addHtmlValue("Description", '<div style=width:170px;margin:0;padding:0><textarea id=dp3meshdesc maxlength=1024 style=width:100%;resize:none></textarea></div>'); setDialogMode(2, "Create Device Group", 3, account_createMeshEx, x); account_validateMeshCreate(); Q('dp3meshname').focus(); } function account_validateMeshCreate() { QE('idx_dlgOkButton', Q('dp3meshname').value.length > 0); } function account_createMeshEx(button, tag) { meshserver.send({ action: 'createmesh', meshname: Q('dp3meshname').value, meshtype: parseInt(Q('dp3meshtype').value), desc: Q('dp3meshdesc').value }); } function account_validateDeleteAccount() { QE('account_dlgOkButton', (Q('apassword1').value.length > 0) && (Q('apassword1').value == Q('apassword2').value)); } function account_validateNewPassword() { var r = '', ok = (Q('apassword0').value.length > 0) && (Q('apassword1').value.length > 0) && (Q('apassword1').value == Q('apassword2').value) && (Q('apassword0').value != Q('apassword1').value); if ((features & 0x00010000) && (Q('apasswordhint').value == Q('apassword1').value)) { ok = false; } if (Q('apassword1').value != '') { if (passRequirements == null || passRequirements == '') { // No password requirements, display password strength var passStrength = checkPasswordStrength(Q('apassword1').value); if (passStrength >= 80) { r = '<span style=color:green>Strong<span>'; } else if (passStrength >= 60) { r = '<span style=color:blue>●<span>'; } else { r = '<span style=color:red>●<span>'; } } else { // Password requirements provided, use that var passReq = checkPasswordRequirements(Q('apassword1').value, passRequirements); if (passReq == false) { ok = false; r = '<span style=color:red>' + "Policy" + '<span>' } } } QH('dxPassWarn', r); //QE('account_dlgOkButton', ok); QE('idx_dlgOkButton', ok); } // Return a password strength score function checkPasswordStrength(password) { var r = 0, letters = {}, varCount = 0, variations = { digits: /\d/.test(password), lower: /[a-z]/.test(password), upper: /[A-Z]/.test(password), nonWords: /\W/.test(password) } if (!password) return 0; for (var i = 0; i < password.length; i++) { letters[password[i]] = (letters[password[i]] || 0) + 1; r += 5.0 / letters[password[i]]; } for (var c in variations) { varCount += (variations[c] == true) ? 1 : 0; } return parseInt(r + (varCount - 1) * 10); } // Check password requirements function checkPasswordRequirements(password, requirements) { if ((requirements == null) || (requirements == '') || (typeof requirements != 'object')) return true; if (requirements.min) { if (password.length < requirements.min) return false; } if (requirements.max) { if (password.length > requirements.max) return false; } var numeric = 0, lower = 0, upper = 0, nonalpha = 0; for (var i = 0; i < password.length; i++) { if (/\d/.test(password[i])) { numeric++; } if (/[a-z]/.test(password[i])) { lower++; } if (/[A-Z]/.test(password[i])) { upper++; } if (/\W/.test(password[i])) { nonalpha++; } } if (requirements.numeric && (numeric < requirements.numeric)) return false; if (requirements.lower && (lower < requirements.lower)) return false; if (requirements.upper && (upper < requirements.upper)) return false; if (requirements.nonalpha && (nonalpha < requirements.nonalpha)) return false; return true; } function updateMeshes() { var r = '', count = 0; for (i in meshes) { count++; // Mesh rights var meshrights = GetMeshRights(meshes[i]); var rights = "Partial Rights"; if (meshrights == 0xFFFFFFFF) rights = "Full Administrator"; else if (meshrights == 0) rights = "No Rights"; // Print the mesh information r += '<div style=cursor:pointer onclick=goForward(\'' + i + '\')>'; r += '<div style="float:left;margin-left:4px"><img src="/images/meshicon50.png" width=50 height=50 /></div>'; r += '<div class=meshList>'; r += '<div><div style=color:black;padding-left:12px;padding-top:2px><b>' + EscapeHtml(meshes[i].name) + '</b></div><div style=padding-left:12px;padding-top:3px;color:black>' + rights + '</div></div>'; r += '</div></div>'; } QH('p3meshes', r); QV('p3noMeshFound', count == 0); } function gotoMesh(meshid) { currentMesh = meshes[meshid]; if (currentMesh == null) { goBack(); } p20updateMesh(); go(20); } // // MY FILES // var filetreelinkpath; var filetreelocation = []; function p5refreshFiles() { meshserver.send({ action: 'files' }); } function updateFiles() { QV('MainMenuMyFiles', ((features & 8) == 0)); if ((features & 8) != 0) return; // If running on a server without files, exit now. var html1 = '', html2 = '', displayPath = '<a style=cursor:pointer;color:black onclick=p5folderup(0)>' + "Root" + '</a>', fullPath = 'Root', publicPath, filetreex = filetree, folderdepth = 1; // Navigate to path location, build the paths at the same time var filetreelocation2 = [], oldlinkpath = filetreelinkpath, checkedBoxes = [], checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { checkedBoxes.push(checkboxes[i].value) }; } // Save all existing checked boxes filetreelinkpath = ''; for (var i in filetreelocation) { if ((filetreex.f != null) && (filetreex.f[filetreelocation[i]] != null)) { filetreelocation2.push(filetreelocation[i]); fullPath += ' / ' + filetreelocation[i]; if ((folderdepth == 1)) { var sp = filetreelocation[i].split('/'); publicPath = window.location + sp[0] + 'files/' + sp[2]; //if (filetreelocation[i] === userinfo._id) { filetreelinkpath += 'self'; } else { filetreelinkpath += (sp[0] + '/' + sp[2]); } filetreelinkpath += filetreelocation[i]; } else { if (filetreelinkpath != '') { filetreelinkpath += '/' + filetreelocation[i]; if (folderdepth > 2) { publicPath += '/' + filetreelocation[i]; } } } filetreex = filetreex.f[filetreelocation[i]]; displayPath += ' / <a style=cursor:pointer;color:black onclick=p5folderup(' + folderdepth + ')>' + EscapeHtml(filetreex.n != null ? filetreex.n : filetreelocation[i]) + '</a>'; folderdepth++; } else { break; } } filetreelocation = filetreelocation2; // In case we could not go down the full path, we set the new path location here. var publicfolder = fullPath.toLowerCase().startsWith('root / ' + userinfo._id + ' / public'); // Sort the files var filetreexx = p5sort_files(filetreex.f); // Display all files and folders at this location for (var i in filetreexx) { // Figure out the name and shortname var f = filetreexx[i], name = f.n, shortname; shortname = name; if (name.length > 40) { shortname = EscapeHtml(name.substring(0, 40)) + "..."; } else { shortname = EscapeHtml(name); } name = EscapeHtml(name); // Figure out the date //var fdatestr = ''; //if (f.d != null) { var fdate = new Date(f.d), fdatestr = (fdate.getMonth() + 1) + '/' + (fdate.getDate()) + '/' + fdate.getFullYear() + ' ' + printTime(fdate) + ' '; } // Figure out the size var fsize = ''; if (f.s != null) { fsize = getFileSizeStr(f.s); } var h = ''; if (f.t < 3 || f.t == 4) { var right = (f.t == 1 || f.t == 4) ? p5getQuotabar(f) : ''; h = '<div class=filelist file=999><input file=999 style=float:left name=fc class=fcb type=checkbox onchange=p5setActions() value=\'' + name + '\'> <span style=float:right;padding-right:4px>' + right + '</span><span><div class=fileIcon' + f.t + '></div><a style=cursor:pointer onclick=p5folderset("' + encodeURIComponent(f.nx) + '")>' + shortname + '</a></span></div>'; } else { var link = shortname; var publiclink = ''; if (publicfolder) { publiclink = ' (<a style=cursor:pointer onclick=\'p5showPublicLink("' + publicPath + '/' + f.nx + '")\'>' + "Link" + '</a>)'; } if (f.s > 0) { link = '<a rel=\"noreferrer noopener\" target=\"_blank\" href=\"downloadfile.ashx?link=' + encodeURIComponent(filetreelinkpath + '/' + f.nx) + '\">' + shortname + '</a>' + publiclink; } h = '<div class=filelist file=3><input file=3 style=float:left name=fc class=fcb type=checkbox onchange=p5setActions() value=\'' + f.nx + '\'> <span style=float:right;padding-right:4px>' + EscapeHtml(fsize) + '</span><span><div class=fileIcon' + f.t + '></div>' + link + '</span></div>'; } if (f.t < 3) { html1 += h; } else { html2 += h; } } //if (f.parent == null) { } QH('p5rightOfButtons', p5getQuotabar(filetreex)); QH('p5files', html1 + html2); QH('p5currentpath', displayPath); QE('p5FolderUp', filetreelocation.length != 0); QV('p5PublicShare', publicfolder); // Re-check all boxes if needed if (oldlinkpath == filetreelinkpath) { checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { checkboxes[i].checked = (checkedBoxes.indexOf(checkboxes[i].value) >= 0); } } p5setActions(); } function getNiceSize(bytes) { if (bytes <= 0) return "Storage exceed"; if (bytes < 2048) return format("{0}b left", bytes); if (bytes < 2097152) return format("{0}k left", Math.round(bytes / 1024)); if (bytes < 2147483648) return format("{0}m left", Math.round(bytes / 1024 / 1024)); return format("{0}g left", Math.round(bytes / 1024 / 1024 / 1024)); } function p5getQuotabar(f) { while (f.t > 1 && f.t != 4) { f = f.parent; } if ((f.t != 1 && f.t != 4) || (f.maxbytes == null)) return ''; return getNiceSize(f.maxbytes - f.s) + ' <progress style=height:10px;width:100px value=' + f.s + ' max=' + f.maxbytes + ' />'; } function p5showPublicLink(u) { setDialogMode(2, "Public Link", 1, null, '<input type=text style=width:100% value="' + u + '" readonly />'); } var sortorder; function p5sort_filename(a, b) { if (a.ln > b.ln) return (1 * sortorder); if (a.ln < b.ln) return (-1 * sortorder); return 0; } function p5sort_timestamp(a, b) { if (a.d > b.d) return (1 * sortorder); if (a.d < b.d) return (-1 * sortorder); return 0; } function p5sort_bysize(a, b) { if (a.s == b.s) return p5sort_filename(a, b); return (((a.s - b.s)) * sortorder); } function p5sort_files(files) { var r = [], sortselection = Q('p5sortdropdown').value; for (var i in files) { files[i].nx = i; if (files[i].n == null) { files[i].n = i; } files[i].ln = files[i].n.toLowerCase(); r.push(files[i]); } sortorder = 1; if (sortselection > 3) { sortorder = -1; sortselection -= 3; } if (sortselection == 1) { r.sort(p5sort_filename); } else if (sortselection == 2) { r.sort(p5sort_bysize); } else if (sortselection == 3) { r.sort(p5sort_timestamp); } return r; } function p5setActions() { var cc = getFileSelCount(), tc = getFileCount(), sfc = getFileSelCount(false); // In order: number of entires selected, number of total entries, number of selected entires that are files (not folders) QE('p5DeleteFileButton', (cc > 0) && (filetreelocation.length > 0)); QE('p5NewFolderButton', filetreelocation.length > 0); QE('p5UploadButton', filetreelocation.length > 0); QE('p5RenameFileButton', (cc == 1) && (filetreelocation.length > 0)); QE('p5SelectAllButton', tc > 0); Q('p5SelectAllButton').value = (cc > 0 ? "None" : "All"); QE('p5CutButton', (sfc > 0) && (cc == sfc)); QE('p5CopyButton', (sfc > 0) && (cc == sfc)); QE('p5PasteButton', (p5clipboard != null) && (p5clipboard.length > 0) && (filetreelocation.length > 0)); } function getFileSelCount(includeDirs) { var cc = 0, checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && ((includeDirs != false) || (checkboxes[i].attributes.file.value == '3'))) cc++; } return cc; } function getFileSelDirCount() { var cc = 0, checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '999')) cc++; } return cc; } function getFileCount() { var cc = 0; var checkboxes = document.getElementsByName('fc'); return checkboxes.length; } function p5selectallfile() { var nv = (getFileSelCount() == 0), checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { checkboxes[i].checked = nv; } p5setActions(); } function setupBackPointers(x) { if (x.f != null) { var fs = 0, fc = 0; for (var i in x.f) { setupBackPointers(x.f[i]); x.f[i].parent = x; if (x.f[i].s) { fs += x.f[i].s; } if (x.f[i].c) { fc += x.f[i].c; } if (x.f[i].t == 3) { fc++; } } x.s = fs; x.c = fc; } return x; } function getFileSizeStr(size) { if (size == 1) return "1 byte"; return format("{0} bytes", size); } function p5folderup(x) { if (x == null) { filetreelocation.pop(); } else { while (filetreelocation.length > x) { filetreelocation.pop(); } } updateFiles(); return false; } function p5folderset(x) { filetreelocation.push(decodeURIComponent(x)); updateFiles(); return false; } function p5createfolder() { setDialogMode(2, "New Folder", 3, p5createfolderEx, '<input type=text id=p5renameinput maxlength=64 onkeyup=p5fileNameCheck(event) style=width:100% />'); focusTextBox('p5renameinput'); p5fileNameCheck(); } function p5createfolderEx() { meshserver.send({ action: 'fileoperation', fileop: 'createfolder', path: filetreelocation, newfolder: Q('p5renameinput').value }); } function p5deletefile() { var cc = getFileSelCount(), rec = (getFileSelDirCount() > 0) ? '<br /><br /><label><input type=checkbox id=p5recdeleteinput>' + "Recursive delete" + '</label><br>' : '<input type=checkbox id=p5recdeleteinput style=\'display:none\'>'; setDialogMode(2, "Delete", 3, p5deletefileEx, (cc > 1) ? (format("Delete {0} selected items?", cc) + rec) : ("Delete selected item?" + rec)); } function p5deletefileEx() { var delfiles = [], checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { delfiles.push(checkboxes[i].value); } } meshserver.send({ action: 'fileoperation', fileop: 'delete', path: filetreelocation, delfiles: delfiles, rec: Q('p5recdeleteinput').checked }); } function p5renamefile() { var renamefile, checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { renamefile = checkboxes[i].value; } } setDialogMode(2, "Rename", 3, p5renamefileEx, '<input type=text id=p5renameinput maxlength=64 onkeyup=p5fileNameCheck(event) style=width:100% value="' + renamefile + '" />', { action: 'fileoperation', fileop: 'rename', path: filetreelocation, oldname: renamefile }); focusTextBox('p5renameinput'); p5fileNameCheck(); } function p5renamefileEx(b, t) { t.newname = Q('p5renameinput').value; meshserver.send(t); } function p5fileNameCheck(e) { var x = isFilenameValid(Q('p5renameinput').value); QE('idx_dlgOkButton', x); if ((x == true) && (e && e.keyCode == 13)) { dialogclose(1); } } var isFilenameValid = (function () { var x1 = /^[^\\/:\*\?"<>\|]+$/, x2 = /^\./, x3 = /^(nul|prn|con|lpt[0-9]|com[0-9])(\.|$)/i; return function isFilenameValid(fname) { return x1.test(fname) && !x2.test(fname) && !x3.test(fname) && (fname[0] != '.'); } })(); function p5uploadFile() { setDialogMode(2, "Upload File", 3, p5uploadFileEx, '<form method=post enctype=multipart/form-data action=uploadfile.ashx target=fileUploadFrame><input type=text name=link style=display:none id=p5uploadpath value="' + encodeURIComponent(filetreelinkpath) + '" /><input type=file name=files id=p5uploadinput style=width:100% multiple=multiple onchange="updateUploadDialogOk(\'p5uploadinput\')" /><input type=hidden name=authCookie value=' + authCookie + ' /><input type=submit id=p5loginSubmit style=display:none /></form>'); updateUploadDialogOk('p5uploadinput'); } function p5uploadFileEx() { Q('p5loginSubmit').click(); } function updateUploadDialogOk(x) { QE('idx_dlgOkButton', Q(x).value != ''); } var p5clipboard = null, p5clipboardFolder = null, p5clipboardCut = 0; function p5copyFile(cut) { var checkboxes = document.getElementsByName('fc'); p5clipboard = []; p5clipboardCut = cut, p5clipboardFolder = Clone(filetreelocation); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '3')) { p5clipboard.push(checkboxes[i].value); } } p5updateClipview(); } function p5pasteFile() { var x = ''; if ((p5clipboard != null) && (p5clipboard.length > 0)) { x = format("Confirm {0} of {1} entrie{2} to this location?", (p5clipboardCut == 0 ? 'copy' : 'move'), p5clipboard.length, ((p5clipboard.length > 1) ? 's' : '')) } setDialogMode(2, "Paste", 3, p5pasteFileEx, x); } function p5pasteFileEx() { meshserver.send({ action: 'fileoperation', fileop: (p5clipboardCut == 0 ? 'copy' : 'move'), scpath: p5clipboardFolder, path: filetreelocation, names: p5clipboard }); p5folderup(999); if (p5clipboardCut == 1) { p5clipboard = null, p5clipboardFolder = null, p5clipboardCut = 0; p5updateClipview(); } } function p5updateClipview() { var x = ''; if ((p5clipboard != null) && (p5clipboard.length > 0)) { x = format("Holding {0} entrie{1} for {2}", p5clipboard.length, ((p5clipboard.length > 1) ? 's' : ''), (p5clipboardCut == 0 ? "copy" : "move")) + ', <a href=# onclick="return p5clearClip()" style=cursor:pointer>' + "Clear" + '</a>.' } QH('p5bottomstatus', x); p5setActions(); } function p5clearClip() { p5clipboard = null; p5clipboardFolder = null; p5clipboardCut = 0; p5updateClipview(); return false; } function p5fileDragDrop(e) { haltEvent(e); QV('bigfail', false); QV('bigok', false); //QV('p5fileCatchAllInput', false); if (e.dataTransfer == null || e.dataTransfer.files.length == 0 || filetreelocation.length == 0) return; var names = [], sizes = [], types = [], datas = [], readercount = e.dataTransfer.files.length; for (var i = 0; i < e.dataTransfer.files.length; i++) { var reader = new FileReader(), file = e.dataTransfer.files[i]; names.push(file.name); sizes.push(file.size); types.push(file.type); reader.onload = function (event) { datas.push(event.target.result); if (--readercount == 0) { Q('p5fileDragName').value = names.join('*'); Q('p5fileDragSize').value = sizes.join('*'); Q('p5fileDragType').value = types.join('*'); Q('p5fileDragData').value = datas.join('*'); Q('p5fileDragLink').value = encodeURIComponent(filetreelinkpath); Q('p5loginSubmit2').click(); } } reader.readAsDataURL(file); } } var p5dragtimer = null; function p5fileDragOver(e) { haltEvent(e); if (p5dragtimer != null) { clearTimeout(p5dragtimer); p5dragtimer = null; } var ac = true; // TODO: Set to true if we can accept the file if (filetreelocation.length == 0) { ac = false; } QV('bigok', ac); QV('bigfail', !ac); //QV('p5fileCatchAllInput', ac); } function p5fileDragLeave(e) { haltEvent(e); if (e.target.id != 'p5filetable') { QV('bigfail', false); QV('bigok', false); //QV('p5fileCatchAllInput', false); } else { p5dragtimer = setTimeout('QV(\'bigfail\',false);QV(\'bigok\',false);p5dragtimer=null;', 200); } } // // MY DEVICES // function onRealNameCheckBox() { showRealNames = Q('RealNameCheckBox').checked; putstore('showRealNames', showRealNames ? 1 : 0); mainUpdate(4); } function onOnlineCheckBox(e) { putstore('onlineOnly', Q('OnlineCheckBox').checked ? 1 : 0); onSearchInputChanged(); } function onDeviceSearchChanged(e) { setTimeout(function () { onSearchInputChanged(); }, 10); } function clearSearchInput() { Q('SearchInput').value = ''; Q('OnlineCheckBox').checked = false; onSearchInputChanged(); } function onSearchInputChanged() { var x = Q('SearchInput').value.toLowerCase().trim(); putstore('_search', Q('SearchInput').value); QS('SearchInput')['background-color'] = (x == '') ? '#FFFFFF' : '#FDFFBE'; var userSearch = null, ipSearch = null, groupSearch = null, tagSearch = null, agentTagSearch = null, wscSearch = null, osSearch = null, amtSearch = null; if (x.startsWith("user:".toLowerCase())) { userSearch = x.substring("user:".length); } else if (x.startsWith("u:".toLowerCase())) { userSearch = x.substring("u:".length); } else if (x.startsWith("ip:".toLowerCase())) { ipSearch = x.substring("ip:".length); } else if (x.startsWith("group:".toLowerCase())) { groupSearch = x.substring("group:".length); } else if (x.startsWith("g:".toLowerCase())) { groupSearch = x.substring("g:".length); } else if (x.startsWith("tag:".toLowerCase())) { tagSearch = Q('SearchInput').value.trim().substring("tag:".length); } else if (x.startsWith("t:".toLowerCase())) { tagSearch = Q('SearchInput').value.trim().substring("t:".length); } else if (x.startsWith("atag:".toLowerCase())) { agentTagSearch = Q('SearchInput').value.trim().substring("atag:".length).toLowerCase(); } else if (x.startsWith("a:".toLowerCase())) { agentTagSearch = Q('SearchInput').value.trim().substring("a:".length).toLowerCase(); } else if (x.startsWith("os:".toLowerCase())) { osSearch = Q('SearchInput').value.trim().substring("os:".length).toLowerCase(); } else if (x.startsWith("amt:".toLowerCase())) { amtSearch = Q('SearchInput').value.trim().substring("amt:".length).toLowerCase(); } else if (x == 'wsc:ok') { wscSearch = 1; } else if (x == 'wsc:noav') { wscSearch = 2; } else if (x == 'wsc:noupdate') { wscSearch = 3; } else if (x == 'wsc:nofirewall') { wscSearch = 4; } else if (x == 'wsc:any') { wscSearch = 5; } if (x == '') { // No search for (var d in nodes) { nodes[d].v = true; } } else if (ipSearch != null) { // IP address search for (var d in nodes) { nodes[d].v = ((nodes[d].ip != null) && (nodes[d].ip.indexOf(ipSearch) >= 0)); } } else if (groupSearch != null) { // Group filter for (var d in nodes) { nodes[d].v = (meshes[nodes[d].meshid].name.toLowerCase().indexOf(groupSearch) >= 0); } } else if (tagSearch != null) { // Tag filter for (var d in nodes) { nodes[d].v = ((nodes[d].tags == null) && (tagSearch == '')) || ((nodes[d].tags != null) && (nodes[d].tags.indexOf(tagSearch) >= 0)); } } else if (agentTagSearch != null) { // Agent Tag filter for (var d in nodes) { nodes[d].v = (((nodes[d].agent != null) && (nodes[d].agent.tag == null)) && (agentTagSearch == '')) || ((nodes[d].agent != null) && (nodes[d].agent.tag != null) && (nodes[d].agent.tag.toLowerCase().indexOf(agentTagSearch) >= 0)); } } else if (userSearch != null) { // User search for (var d in nodes) { nodes[d].v = false; if (nodes[d].users && nodes[d].users.length > 0) { for (var i in nodes[d].users) { if (nodes[d].users[i].toLowerCase().indexOf(userSearch) >= 0) { nodes[d].v = true; } } } } } else if (osSearch != null) { // OS search for (var d in nodes) { nodes[d].v = ((nodes[d].osdesc != null) && (nodes[d].osdesc.toLowerCase().indexOf(osSearch) >= 0)); } } else if (amtSearch != null) { // Intel AMT search for (var d in nodes) { nodes[d].v = (nodes[d].intelamt != null) && ((amtSearch == '') || (nodes[d].intelamt.state == amtSearch)); } } else if (wscSearch != null) { // Windows Security Center for (var d in nodes) { nodes[d].v = false; if (nodes[d].wsc) { if ((wscSearch == 1) && (nodes[d].wsc.antiVirus == 'OK') && (nodes[d].wsc.autoUpdate == 'OK') && (nodes[d].wsc.firewall == 'OK')) { nodes[d].v = true; } else if (((wscSearch == 2) || (wscSearch == 5)) && (nodes[d].wsc.antiVirus != 'OK')) { nodes[d].v = true; } else if (((wscSearch == 3) || (wscSearch == 5)) && (nodes[d].wsc.autoUpdate != 'OK')) { nodes[d].v = true; } else if (((wscSearch == 4) || (wscSearch == 5)) && (nodes[d].wsc.firewall != 'OK')) { nodes[d].v = true; } } } } else if (x == '*') { // Star filter for (var d in nodes) { nodes[d].v = (stars[nodes[d]._id] == 1); } } else { // Device name search try { var rs = x.split(/\s+/).join('|'), rx = new RegExp(rs); // In some cases (like +), this can throw an exception. for (var d in nodes) { nodes[d].v = (rx.test(nodes[d].name.toLowerCase())) || (nodes[d].rnamel != null && rx.test(nodes[d].rnamel.toLowerCase())); if ((nodes[d].v == false) && nodes[d].tags) { for (var s in nodes[d].tags) { if (rx.test(nodes[d].tags[s].toLowerCase())) { nodes[d].v = true; break; } else { nodes[d].v = false; } } } } } catch (ex) { for (var d in nodes) { nodes[d].v = true; } } } // Check power state var onlineOnly = Q('OnlineCheckBox').checked; if (onlineOnly) { for (var d in nodes) { if ((nodes[d].conn == null) || (nodes[d].conn == 0)) { nodes[d].v = false; } } } mainUpdate(4); } var gotKeyPressEvent = false; function ondeskkeypress(e, t) { setSessionActivity(); if (desktop && !xxdialogMode && (xxcurrentView == 10) && (currentDevicePanel == 1)) { gotKeyPressEvent = true; Q('softKeyboard').value = ''; // Check what keys we are allows to send if (currentNode != null) { var meshrights = GetMeshRights(currentNode.meshid); var inputAllowed = ((features2 & 0x2000) == 0) && ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0))); if (inputAllowed == false) return false; var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0))); if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; } } return desktop.m.handleKeys(e); } if (terminal && !xxdialogMode && (xxcurrentView == 10) && (currentDevicePanel == 5) && (t !== 1)) { if (e.altKey == true) { return true; } gotKeyPressEvent = true; Q('softKeyboard').value = ''; var k = 0; if (e.charCode != 0) { k = e.charCode; } else if (e.keyCode != 0) { k = e.keyCode; } if (k != 0) { if (terminal.urlname == 'sshterminalrelay.ashx') { // SSH terminal.socket.send('~' + String.fromCharCode(k)); } else { // Agent terminal.sendText(String.fromCharCode(k)); } } return false; } } function ondeskkeydown(e, t) { setSessionActivity(); if (desktop && !xxdialogMode && (xxcurrentView == 10) && (currentDevicePanel == 1)) { gotKeyPressEvent = false; Q('softKeyboard').value = ''; // Check what keys we are allows to send if (currentNode != null) { var meshrights = GetMeshRights(currentNode.meshid); var inputAllowed = ((features2 & 0x2000) == 0) && ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0))); if (inputAllowed == false) return false; var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0))); if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; } } return desktop.m.handleKeyDown(e); } if (terminal && !xxdialogMode && (xxcurrentView == 10) && (currentDevicePanel == 5) && (t !== 1)) { if (e.altKey == true) { return true; } Q('softKeyboard').value = ''; gotKeyPressEvent = false; var k = 0; if (e.charCode != 0) { k = e.charCode; } else if (e.keyCode != 0) { k = e.keyCode; } if (k == 8) { terminal.sendText(String.fromCharCode(k)); } // Enter and backspace else if (e.ctrlKey && (k >= 64) && (k <= 95)) { // Ctrl keys if (terminal.urlname == 'sshterminalrelay.ashx') { // SSH terminal.socket.send('~' + String.fromCharCode(k - 64)); } else { // Agent terminal.sendText(String.fromCharCode(k - 64)); } } } } function ondeskkeyup(e, t) { setSessionActivity(); if (desktop && !xxdialogMode && (xxcurrentView == 10) && (currentDevicePanel == 1)) { var inputStr = Q('softKeyboard').value; Q('softKeyboard').value = ''; // Check what keys we are allows to send if (currentNode != null) { var meshrights = GetMeshRights(currentNode.meshid); var inputAllowed = ((features2 & 0x2000) == 0) && ((meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0))); if (inputAllowed == false) return false; var limitedInputAllowed = ((meshrights != 0xFFFFFFFF) && (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) != 0))); if (limitedInputAllowed == true) { if ((e.altKey == true) || (e.ctrlKey == true) || ((e.keyCode < 32) && (e.keyCode != 8) && (e.keyCode != 13)) || (e.keyCode > 90)) return false; } } if ((gotKeyPressEvent == false) && (inputStr.length > 0) && desktop.m.SendKeyUnicode) { // This is a mobile keyboard, we need to send that is in the input control. var inputchar = inputStr[inputStr.length - 1].charCodeAt(0); desktop.m.SendKeyUnicode(desktop.m.KeyAction.DOWN, inputchar); desktop.m.SendKeyUnicode(desktop.m.KeyAction.UP, inputchar); } else { return desktop.m.handleKeyUp(e); } } if (terminal && !xxdialogMode && (xxcurrentView == 10) && (currentDevicePanel == 5) && (gotKeyPressEvent == false) && (t !== 1)) { if (e.altKey == true) { return true; } var inputStr = Q('softKeyboard').value; Q('softKeyboard').value = ''; if (terminal.urlname == 'sshterminalrelay.ashx') { // SSH terminal.socket.send('~' + inputStr); } else { // Agent if (inputStr) terminal.sendText(inputStr); } return false; } } var sort = 0; var deviceHeaderId = 0; var deviceHeaderCount; var deviceHeaders = {}; var showRealNames = false; var deviceHeaderTotal = 0; var deviceHeaders = {}; var deviceHeadersTitles = {}; function updateDevices() { var r = '', c = 0, current = null, count = 0, displayedMeshes = {}, groups = {}, groupCount = {}; // 3 wide, list view or desktop view deviceHeaderId = 0; deviceHeaderCount = {}; deviceHeaderTotal = 0; deviceHeaders = {}; deviceHeadersTitles = {}; var current; // Perform node sort if (sort == 0) { nodes.sort(meshSort); } else if (sort == 1) { nodes.sort(powerSort); } else if (sort == 2) { if (showRealNames == true) { nodes.sort(deviceHostSort); } else { nodes.sort(deviceSort); } } // Go thru the list of nodes and display them for (var i in nodes) { if (nodes[i].v == false) continue; //var meshrights = GetNodeRights(nodes[i]); if (sort == 0) { // Mesh header nodes.sort(meshSort); //if (nodes[i].meshid != current) { if (((meshes[nodes[i].meshid]?nodes[i].meshid:'*') != current)) { deviceHeaderSet(); var extra = ''; if ((meshes[nodes[i].meshid] != null) && (meshes[nodes[i].meshid].mtype == 1)) { extra = '<span style=color:lightgray>' + ", Intel® AMT only" + '</span>'; } if (current != null) { if (c == 2) { r += '<td><div style=width:301px></div></td>'; } if (r != '') { r += '</tr></table>'; } } r += '<div class=DevSt style=padding-top:4px><span style=float:right>'; //r += getMeshActions(meshes[nodes[i].meshid], meshrights); if (meshes[nodes[i].meshid]) { r += '</span><span id=MxMESH style=cursor:pointer onclick=goForward("' + nodes[i].meshid + '")>' + EscapeHtml(meshes[nodes[i].meshid].name) + '</span>' + extra + '<span id=DevxHeader' + deviceHeaderId + ' style=color:lightgray></span></div>'; current = nodes[i].meshid; } else { r += '</span><span id=MxMESH><i>' + "Individual Devices" + '</i></span><span id=DevxHeader' + deviceHeaderId + ' style=color:lightgray></span></div>'; current = '*'; } displayedMeshes[current] = 1; c = 0; } } else if (sort == 1) { // Power header if (nodes[i].pwr !== current) { deviceHeaderSet(); if (current !== null) { if (c == 2) { r += '<td><div style=width:301px></div></td>'; } if (r != '') { r += '</tr></table>'; } } r += '<div class=DevSt style=width:100%;padding-top:4px><span>' + PowerStateStr2(nodes[i].pwr) + '</span><span id=DevxHeader' + deviceHeaderId + ' style=color:lightgray></span></div>'; current = nodes[i].pwr; c = 0; } } else if (sort == 2) { // Device header if (current == null) { current = '1'; } } count++; r += '<div name=xxdevice onclick=goForward(\'' + nodes[i]._id + '\') class=devList1 id=\'' + nodes[i]._id + '\'></div>'; // This is a standin for the device, it gets rendered only if visible. // If we are displaying devices by group, put the device in the right group. /* if ((sort == 3) && (r != '')) { if (nodes[i].tags) { for (var j in nodes[i].tags) { var tag = nodes[i].tags[j]; if (groups[tag] == null) { groups[tag] = r; groupCount[tag] = 1; } else { groups[tag] += r; groupCount[tag] += 1; } if (view == 3) break; } } r = ''; } */ deviceHeaderTotal++; if (typeof deviceHeaderCount[nodes[i].state] == 'undefined') { deviceHeaderCount[nodes[i].state] = 1; } else { deviceHeaderCount[nodes[i].state]++; } } // If there is nothing to display, explain the problem var viewNothing = false; if ((r == '') && (nodes.length > 0) && (Q('SearchInput').value != '')) { viewNothing = true; r = '<div style="margin:30px">' + "No devices matching this search." + '</div>'; } // Display all empty device groups, we need to do this because users can add devices to these at any time. if ((sort == 0) && (Q('SearchInput').value == '')) { for (var i in meshes) { var mesh = meshes[i]; if ((displayedMeshes[mesh._id] == null) && (IsMeshViewable(mesh))) { if ((current != '') && (r != '')) { r += '</tr></table>'; } r += '<div><div colspan=3 class=DevSt><span style=float:right>'; //r += getMeshActions(mesh, meshrights); r += '</span><span id=MxMESH style=cursor:pointer onclick=goForward("' + mesh._id + '")>' + EscapeHtml(mesh.name) + '</span></div>'; if (mesh.mtype == 1) { r += '<div style=padding:10px><i>' + "No Intel® AMT devices in this group"; } if (mesh.mtype == 2) { r += '<div style=padding:10px><i>' + "No devices in this group"; } r += '.</i></div></div>'; current = mesh._id; count++; } } } if (count == 0) { if ((Q('SearchInput').value != '') || (Q('OnlineCheckBox').checked)) { QH('xdevices', '<div style="margin-top:50px;text-align:center"><span style="font-size:30px">' + "No devices" + '</span><br /><br />' + "No devices matching this search." + ' <a onclick=clearSearchInput() style=cursor:pointer>' + "Clear search filter" + '</a></div>'); } else { QH('xdevices', '<div style="margin-top:50px;text-align:center"><span style="font-size:30px">' + "No devices" + '</span><br /><br />' + "Use the desktop version of this website to add devices." + '</div>'); } } else { QH('xdevices', r); } deviceHeaderSet(); for (var i in deviceHeaders) { QH(i, deviceHeaders[i]); } for (var i in deviceHeadersTitles) { Q(i).title = deviceHeadersTitles[i]; } onDevicesScrollEx(); } var onDevicesTouchActive = false; var onDevicesScrollnagleTimer = null; function onDevicesScroll() { if (onDevicesScrollnagleTimer == null) { onDevicesScrollnagleTimer = setTimeout(onDevicesScrollEx, 250); } } function onDeviceTouch(x) { if (onDevicesTouchActive == x) return; onDevicesTouchActive = x; if (x == false) onDevicesScrollEx(); } function onDevicesScrollEx() { var devdivs = document.getElementsByName('xxdevice'); onDevicesScrollnagleTimer = null; for (var i = 0; i < devdivs.length; i++) { // Show var node = getNodeFromId(devdivs[i].id) if (node == null) break; updateDeviceViewHtml(devdivs[i], node); } } // Update a single device in the current view function updateDeviceViewDevice(node) { if (node == null) return; var devdiv = Q(node._id); if ((devdiv != null) && (devdiv.innerHTML != '')) { updateDeviceViewHtml(devdiv, node); } // Only update if the device is visible } function updateDeviceViewHtml(div, node) { var visibleTop = Q('xdevices').scrollTop - 250, visibleBottom = Q('xdevices').scrollTop + Q('xdevices').clientHeight + 250; if ((div.offsetTop >= visibleTop) && (div.offsetTop < visibleBottom)) { var title = EscapeHtml(node.name); if (title.length == 0) { title = '<i>' + "None" + '</i>'; } if ((node.rname != null) && (node.rname.length > 0)) { title += ' / ' + EscapeHtml(node.rname); } var name = EscapeHtml(node.name); if (showRealNames == true && node.rname != null) name = EscapeHtml(node.rname); if (name.length == 0) { name = '<i>' + "None" + '</i>'; } // Add device notification icons var devNotify = '', devNotifySub = ''; // This device is "starred" if (stars[node._id] == 1) { devNotifySub += '<img class=deviceNotifyDotSub src=images/icon-star-notify-16.png width=16 height=16>'; } // This device has session information if (node.sessions != null) { // Display any agent messages if (node.sessions.msg != null) { devNotifySub += '<div style="width:16;height:16" class=deviceNotifyDotSub>' + Object.keys(node.sessions.msg).length + '</div>'; } // Sessions are active if ((node.sessions.kvm != null) || (node.sessions.terminal != null) || (node.sessions.files != null) || (node.sessions.tcp != null) || (node.sessions.udp != null)) { devNotifySub += '<img class=deviceNotifyDotSub src=images/icon-relay-notify.png width=16 height=16>'; } // Help is required if (node.sessions.help != null) { devNotifySub += '<img class=deviceNotifyDotSub src=images/icon-help-notify-16.png width=16 height=16>'; } // Battery state if (node.sessions.battery != null) { var bat = node.sessions.battery; var statestr = ''; if (bat.state == 'ac') { statestr = "Device is plugged-in"; } else if (bat.state == 'dc') { statestr = "Device is battery powered"; } var levelstr = '', levelnum = -1; if ((typeof bat.level == 'number') && (bat.level >= 0) && (bat.level <= 100)) { levelstr = bat.level + '%'; levelnum = (Math.floor((bat.level + 10) / 25) + 1); if (levelnum > 5) { lvl = 5; } if (bat.state == 'ac') { if (bat.level == 100) { levelnum = 11; } else { levelnum += 5; } } } if (levelnum > 0) { devNotify += '<div class="deviceBatterySmall deviceBatterySmall' + levelnum + '" title="' + ((statestr != null) ? (statestr + ', ' + levelstr) : levelstr) + '"></div>'; } } } // Add any device icons if (devNotifySub != '') { devNotify += '<div class=deviceNotifyDot>' + devNotifySub + '</div>'; } // Node var icon = node.icon, nodestate = NodeStateStr(node); if (((!node.conn) || (node.conn == 0)) && (node.mtype != 3)) { icon += ' gray'; } div.innerHTML = '<div>' + devNotify + '<div class="i' + icon + ' devList2"></div><div class=devList3><div class=devList4><b>' + name + '</b></div><div class=devList5>' + nodestate + '</div></div></div>'; } else { div.innerHTML = ''; // Hide } } // Show device help requests function showDeviceHelpRequests(nodeid, force, e) { if (e) haltEvent(e); if (xxdialogMode && !force) return false; var node = null, x = ''; if (nodeid == null) { node = currentNode; } else { node = getNodeFromId(nodeid); } if ((node == null) || (node.sessions == null)) { setDialogMode(0); return false; } if (node.sessions.help != null) { for (var j in node.sessions.help) { x += '<div style=margin-bottom:6px><b>' + EscapeHtml(j) + '</b></div><div style=margin-bottom:6px>' + EscapeHtml(node.sessions.help[j]) + '</div>'; } } if (x != '') { setDialogMode(2, "Help Requests" + ' - ' + EscapeHtml(node.name), 1, null, x, 'HELPREQ-' + node._id); } else { setDialogMode(0); } return false; } // Show currently active sessions on this device function showDeviceSessions(nodeid, force, e) { if (((force !== true) && xxdialogMode) || (currentNode == null)) return; var node = currentNode, x = ''; for (var i in node.sessions) { if ((i == 'kvm') && (node.sessions.multidesk == null)) { x += '<u>' + "Remote Desktop" + '</u>'; for (var j in node.sessions.kvm) { x += addHtmlValue4(getUserName(j), (node.sessions.kvm[j] == 1) ? "1 session" : nobreak(format("{0} sessions", node.sessions.kvm[j]))); } } else if (i == 'multidesk') { x += '<u>' + "Remote Desktop" + '</u>'; for (var j in node.sessions.multidesk) { x += addHtmlValue4(getUserName(j), ((node.sessions.multidesk[j] == 1) ? "1 session" : nobreak(format("{0} sessions", node.sessions.multidesk[j])))); } } else if (i == 'terminal') { x += '<u>' + "Terminal" + '</u>'; for (var j in node.sessions.terminal) { x += addHtmlValue4(getUserName(j), ((node.sessions.terminal[j] == 1) ? "1 session" : nobreak(format("{0} sessions", node.sessions.terminal[j])))); } } else if (i == 'files') { x += '<u>' + "Files" + '</u>'; for (var j in node.sessions.files) { x += addHtmlValue4(getUserName(j), ((node.sessions.files[j] == 1) ? "1 session" : nobreak(format("{0} sessions", node.sessions.files[j])))); } } else if (i == 'tcp') { x += '<u>' + "TCP Routing" + '</u>'; for (var j in node.sessions.tcp) { x += addHtmlValue4(getUserName(j), ((node.sessions.tcp[j] == 1) ? "1 session" : nobreak(format("{0} sessions", node.sessions.tcp[j])))); } } else if (i == 'udp') { x += '<u>' + "UDP Routing" + '</u>'; for (var j in node.sessions.udp) { x += addHtmlValue4(getUserName(j), ((node.sessions.udp[j] == 1) ? "1 session" : nobreak(format("{0} sessions", node.sessions.udp[j])))); } } } if (x != '') { setDialogMode(2, "Sessions" + ' - ' + EscapeHtml(node.name), 1, null, x, 'SESSIONS-' + node._id); } else { setDialogMode(0); } } // Show currently active sessions on this device function showDeviceMessages(nodeid, force, e) { if (e) haltEvent(e); if (xxdialogMode && !force) return false; var node = null, x = '<div style=max-height:200px;width:100%;overflow-y:auto;overflow-x:hidden>', count = 0; if (nodeid == null) { node = currentNode; } else { node = getNodeFromId(nodeid); } if ((node == null) || (node.sessions == null) || (node.sessions.msg == null)) { setDialogMode(0); return false; } for (var i in node.sessions.msg) { var msg = i, icon = 5; if (typeof node.sessions.msg[i].msg == 'string') { msg = node.sessions.msg[i].msg; } if (typeof node.sessions.msg[i].icon == 'number') { icon = node.sessions.msg[i].icon; } if ((icon < 1) || (icon > 9)) { icon = 5; } x += '<table style=width:100%><td style=width:24px><div class=NotifyIconSmall' + icon + '></div><td><div style="border-radius:5px;background-color:#BBB;width:calc(100% - 18px);padding:8px">' + EscapeHtml(msg) + '</div></table>'; count++; } x += '</div>'; if (count > 0) setDialogMode(2, "Agent Messages" + ' - ' + EscapeHtml(node.name), 1, null, x, 'MESSAGES-' + node._id); return false; } var powerStatetable = ['', "Powered", "Sleep", "Sleep", "Sleep", "Hibernating", "Power off", "Present"]; var powerStateStrings = ['', "Powered", "Sleeping", "Sleeping", "Deep Sleep", "Hibernating", "Soft-Off", "Present"]; var powerStateStrings2 = ['', "Device is powered", "Device is in sleep state (S1)", "Device is in sleep state (S2)", "Device is in deep sleep state (S3)", "Device is hibernating (S4)", "Device is in soft-off state (S5)", "Device is present, but power state cannot be determined"]; var powerColorTable = ['#00000000', 'black', 'blue', 'blue', 'lightblue', 'blueviolet', 'darkgreen', 'lightseagreen', 'lightseagreen']; function NodeStateStr(node) { var states = []; if (node.state > 0 && node.state < powerStatetable.length) state.push(powerStatetable[node.state]); if (node.conn) { if ((node.conn & 1) != 0) { states.push('<span>' + "Agent" + '</span>'); } if ((node.conn & 2) != 0) { states.push('<span>' + "CIRA" + '</span>'); } else if ((node.conn & 4) != 0) { states.push('<span>' + "Intel® AMT" + '</span>'); } if ((node.conn & 8) != 0) { states.push('<span>' + "Relay" + '</span>'); } if ((node.conn & 16) != 0) { states.push('<span>' + "MQTT" + '</span>'); } } if ((node.pwr != null) && (node.pwr != 0)) { states.push(powerStateStrings[node.pwr]); } return states.join(', '); } function PowerStateStr(x) { if (x < powerStatetable.length) return powerStatetable[x]; return ''; } function PowerStateStr2(x) { if ((x != 0) && (x < powerStatetable.length)) return powerStatetable[x]; return "Unknown"; } function onSortSelectChange(skipsave) { sort = document.getElementById('sortselect').selectedIndex; if (!skipsave) { putstore('sort', sort); } mainUpdate(4); } function deviceHeaderSet() { if (deviceHeaderId == 0) { deviceHeaderId = 1; return; } deviceHeaders['DevxHeader' + deviceHeaderId] = ', ' + deviceHeaderTotal + ((deviceHeaderTotal == 1) ? " node" : " nodes"); var title = ''; for (var x in deviceHeaderCount) { if (title.length > 0) title += ', '; title += deviceHeaderCount[x] + ' ' + PowerStateStr2(x); } deviceHeadersTitles['DevxHeader' + deviceHeaderId] = title; deviceHeaderId++; deviceHeaderCount = {}; deviceHeaderTotal = 0; } /* function meshSort(a, b) { if (a.meshnamel > b.meshnamel) return 1; if (a.meshnamel < b.meshnamel) return -1; if (a.meshid == b.meshid) { if (showRealNames == true) { if (a.rnamel > b.rnamel) return 1; if (a.rnamel < b.rnamel) return -1; return 0; } else { if (a.namel > b.namel) return 1; if (a.namel < b.namel) return -1; return 0; } } return 0; } function powerSort(a, b) { var ap = a.pwr ? a.pwr : 0; var bp = b.pwr ? b.pwr : 0; if (ap == bp) { if (showRealNames == true) { if (a.rnamel > b.rnamel) return 1; if (a.rnamel < b.rnamel) return -1; return 0; } else { if (a.namel > b.namel) return 1; if (a.namel < b.namel) return -1; return 0; } } if (ap > bp) return 1; if (ap < bp) return -1; return 0; } function deviceSort(a, b) { if (a.namel > b.namel) return 1; if (a.namel < b.namel) return -1; return 0; } function deviceHostSort(a, b) { if (a.rnamel > b.rnamel) return 1; if (a.rnamel < b.rnamel) return -1; return 0; } */ var sortCollator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }) function meshSort(a, b) { var x = sortCollator.compare(a.meshnamel, b.meshnamel); if (x != 0) return x; x = sortCollator.compare(a.meshid, b.meshid); if (x != 0) return x; if (showRealNames == true) { return sortCollator.compare(a.rnamel, b.rnamel); } return sortCollator.compare(a.namel, b.namel); } function powerSort(a, b) { var ap = a.pwr ? a.pwr : 0; var bp = b.pwr ? b.pwr : 0; if (ap > bp) return -1; if (ap < bp) return 1; if (ap == bp) { if (showRealNames == true) { return sortCollator.compare(a.rnamel, b.rnamel); } else { return sortCollator.compare(a.namel, b.namel); } } } function deviceSort(a, b) { return sortCollator.compare(a.namel, b.namel); } function deviceHostSort(a, b) { return sortCollator.compare(a.rnamel, b.rnamel); } // // MY DEVICE // function refreshDevice(nodeid) { if (!currentNode || currentNode._id != nodeid) return; gotoDevice(nodeid, xxcurrentView, true); } var currentDevicePanel = 0; var currentNode; var powerTimelineNode = null; var powerTimelineReq = null; var powerTimelineUpdate = null; var powerTimeline = null; function getCurrentNode() { return currentNode; }; function gotoDevice(nodeid, panel, refresh) { // Remind the user to verify the email address if ((userinfo.emailVerified !== true) && (serverinfo.emailcheck == true) && (userinfo.siteadmin != 0xFFFFFFFF)) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until a email address is verified. This is required for password recovery. Go to the \"My Account\" to change and verify an email address."); return; } // Remind the user to add two factor authentication if ((features & 0x00040000) && !((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0) || (userinfo.otpkeys > 0) || ((features & 0x00800000) && (userinfo.otpekey == 1)))) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until two-factor authentication is enabled. This is required for extra security. Go to the \"My Account\" and look at the \"Account Security\" section."); return; } var node = getNodeFromId(nodeid); if (node == null) { goBack(); return; } var mesh = meshes[node.meshid]; var meshrights = GetNodeRights(node); var deviceSwitch = ((currentNode == null) || (currentNode._id != nodeid)); if (!currentNode || currentNode._id != node._id || refresh == true) { currentNode = node; // Setup session notification QV('p10deviceNotify', (currentNode.sessions != null) && ((node.sessions.kvm != null) || (node.sessions.terminal != null) || (node.sessions.files != null) || (node.sessions.tcp != null) || (node.sessions.udp != null))); QV('p10deviceStar', stars[currentNode._id] == 1); QV('p10deviceHelp', (currentNode.sessions != null) && (currentNode.sessions.help != null)) if ((currentNode.sessions != null) && (currentNode.sessions.msg != null)) { QV('p10deviceMsg', true); QH('p10deviceMsg', Object.keys(currentNode.sessions.msg).length); } else { QV('p10deviceMsg', false); } // Device Battery QV('p10deviceBattery', false); if ((currentNode.sessions != null) && (currentNode.sessions.battery != null)) { var bat = currentNode.sessions.battery; var statestr = ''; if (bat.state == 'ac') { statestr = "Device is plugged-in"; } if (bat.state == 'dc') { statestr = "Device is battery powered"; } var levelstr = '', levelnum = -1; if ((typeof bat.level == 'number') && (bat.level >= 0) && (bat.level <= 100)) { levelstr = bat.level + '%'; levelnum = (Math.floor((bat.level + 10) / 25) + 1); if (levelnum > 5) { lvl = 5; } if (bat.state == 'ac') { if (bat.level == 100) { levelnum = 11; } else { levelnum += 5; } } } if (levelnum > 0) { Q('p10deviceBattery').title = (statestr != null) ? (statestr + ', ' + levelstr) : levelstr; QV('p10deviceBattery', true); Q('p10deviceBattery').className = 'deviceBatteryLarge deviceBatteryLarge' + levelnum; } } else { QV('p10deviceBattery', false); } // Add node name var nname = EscapeHtml(node.name); if (nname.length == 0) { nname = '<i>' + "None" + '</i>'; } if ((meshrights & 4) != 0) { nname = '<span onclick=showEditNodeValueDialog(0) style=cursor:pointer>' + nname + '</span>'; } QH('p10deviceName', nname); // Node attributes var x = '<table style=width:100%>'; // Attribute: Mesh if (mesh) { x += addDeviceAttribute('<span>' + "Group" + '</span>', '<a onclick=goForward("' + node.meshid + '") style=cursor:pointer>' + EscapeHtml(meshes[node.meshid].name) + '</a>'); } // Attribute: Name if (node.rname != null) { x += addDeviceAttribute('<span>' + "Name" + '</span>', '<span>' + EscapeHtml(node.rname) + '</span>'); } // Attribute: Host if ((features & 1) == 0) { // If not WAN-only, local hostname is in use if ((meshrights & 4) != 0) { if (node.host) { x += addDeviceAttribute("Hostname", '<span onclick=showEditNodeValueDialog(1) style=cursor:pointer>' + EscapeHtml(node.host) + '</span>'); } else { x += addDeviceAttribute("Hostname", '<span onclick=showEditNodeValueDialog(1) style=cursor:pointer><i>' + "None" + '</i></span>'); } } else { x += addDeviceAttribute("Hostname", EscapeHtml(node.host)); } } // Attribute: Description var description = node.desc ? EscapeHtml(node.desc) : '<i>' + "None" + '</i>'; if ((meshrights & 4) != 0) { x += addDeviceAttribute("Description", '<span onclick=showEditNodeValueDialog(2) style=cursor:pointer>' + description + '</span>'); } else { x += addDeviceAttribute("Description", description); } // Attribute: Mesh Agent if ((node.agent != null) && (node.agent.id != null) && (node.mtype == 3)) { if (node.agent.id == 4) { x += addDeviceAttribute("Device Type", "Windows"); } if (node.agent.id == 6) { x += addDeviceAttribute("Device Type", "Linux"); } if (node.agent.id == 29) { x += addDeviceAttribute("Device Type", "macOS"); } } else if ((node.agent != null) && (node.agent.id != null) && (node.agent.ver != null)) { var str = ''; if (node.agent.id <= agentsStr.length) { str = agentsStr[node.agent.id]; } else { str = agentsStr[0]; } if (node.agent.ver != 0) { str += ' v' + node.agent.ver; } if (node.agent.id == 14) { str = node.agent.core; } if ((node.agent.root === false) && ((node.conn & 1) != 0)) { str += ', ' + "Restricted"; } x += addDeviceAttribute("Mesh Agent", str); } // Attribute: Intel AMT if (node.intelamt != null) { var str = ''; var provisioningStates = { 0: nobreak("Not Activated (Pre)"), 1: nobreak("Not Activated (In)"), 2: nobreak("Activated") }; if (node.intelamt.ver != null && node.intelamt.state == null) { str += '<i>' + nobreak("Unknown State") + '</i>, v' + EscapeHtml(node.intelamt.ver); } else if ((node.intelamt.ver == null) && (node.intelamt.state == 2)) { str += '<i>' + "Activated" + '</i>'; } else if ((node.intelamt.ver == null) || (node.intelamt.state == null)) { str += '<i>' + "Unknown Version & State" + '</i>'; } else { str += provisioningStates[node.intelamt.state]; if (node.intelamt.flags) { if (node.intelamt.flags & 2) { str = ' <span>' + "CCM" + '</span>'; } else if (node.intelamt.flags & 4) { str = ' <span>' + "ACM" + '</span>'; } } str += (', v' + EscapeHtml(node.intelamt.ver)); } // If Intel AMT is activated, show additional options if (node.intelamt.state == 2) { if (node.intelamt.tls == 1) { str += ', <span title="' + "Intel® AMT is setup with TLS network security" + '">' + "TLS" + '</span>'; } var editUserCredentialsIcon = false; if (node.intelamt.user == null || node.intelamt.user == '') { // If credentials are not set, allow setting them. if ((meshrights & 4) != 0) { str += ', <i style=color:#FF0000;cursor:pointer title="' + "Edit Intel® AMT credentials" + '" onclick=editDeviceAmtSettings("' + node._id + '")>' + "No Credentials" + '</i>'; editUserCredentialsIcon = true; } else { str += ', <i style=color:#FF0000>' + "No Credentials" + '</i>'; } } else if (((features2 & 1) != 0) && (node.intelamt.warn != null)) { // If AMT manager is running and warned of invalid credentials, allow setting them. var warn = null; if ((node.intelamt.warn & 1) != 0) { warn = "Invalid Credentials"; } if ((node.intelamt.warn & 8) != 0) { warn = "Trying Credentials"; } if (warn != null) { if ((meshrights & 4) != 0) { str += ', <i style=color:#FF0000;cursor:pointer title="' + "Edit Intel® AMT credentials" + '" onclick=editDeviceAmtSettings("' + node._id + '")>' + warn + '</i>'; editUserCredentialsIcon = true; } else { str += ', <i style=color:#FF0000>' + warn + '</i>'; } } } // If the AMT manager is not running, always allow Intel AMT credentials to be edited. if (((meshrights & 4) != 0) && ((features2 & 1) == 0)) { editUserCredentialsIcon = true; } str += ' '; if (editUserCredentialsIcon) { str += '<img src=images/link4.png height=10 width=10 title="' + "Edit Intel® AMT credentials" + '" style=cursor:pointer onclick=editDeviceAmtSettings("' + node._id + '")>'; } } /* if (node.intelamt.state == 2) { if (node.intelamt.user == null || node.intelamt.user == '') { if ((meshrights & 4) != 0) { str += ', <i style=color:#FF0000;cursor:pointer onclick=editDeviceAmtSettings("' + node._id + '")>' + nobreak("No Credentials") + '</i>'; } else { str += ', <i style=color:#FF0000>' + "No Credentials" + '</i>'; } } str += ' '; if ((meshrights & 4) != 0) { str += '<img src=images/link4.png height=10 width=10 style=cursor:pointer onclick=editDeviceAmtSettings("' + node._id + '")>'; } } */ var meName = "Intel® ME"; if (typeof node.intelamt.sku == 'number') { if ((node.intelamt.sku & 8) != 0) { meName = "Intel® AMT"; } else if ((node.intelamt.sku & 16) != 0) { meName = "Intel® SM"; } } x += addDeviceAttribute(meName, str); } // Attribute: Mesh Agent Tag if ((node.agent != null) && (node.agent.tag != null) && (node.agent.tag != 'mailto:')) { var tag = EscapeHtml(node.agent.tag); if (tag.startsWith('mailto:')) { tag = '<a href="' + tag + '">' + tag.substring(7) + '</a>'; } x += addDeviceAttribute("Agent Tag", tag); } // Attribute: Intel AMT //if (node.intelamt && node.intelamt.user) { x += addDeviceAttribute('Intel® AMT', node.intelamt.user); } // Attribute: Connectivity (Only show this if more than just the agent is connected). var connectivity = node.conn; if (connectivity && connectivity > 1) { var cstate = []; if ((node.conn & 1) != 0) cstate.push('<span>' + "Agent" + '</span>'); if ((node.conn & 2) != 0) cstate.push('<span>' + "Intel® AMT CIRA" + '</span>'); else if ((node.conn & 4) != 0) cstate.push('<span>' + "Intel® AMT" + '</span>'); if ((node.conn & 8) != 0) cstate.push('<span>' + "Agent Relay" + '</span>'); if ((node.conn & 16) != 0) cstate.push('<span>' + "MQTT" + '</span>'); x += addDeviceAttribute("Connectivity", cstate.join(', ')); } // Node tags var groupingTags = '<i>' + "None" + '</i>'; if (node.tags != null) { groupingTags = ''; for (var i in node.tags) { groupingTags += '<span style="background-color:lightgray;padding:3px;border-radius:5px">' + EscapeHtml(node.tags[i]) + '</span> '; } } if ((meshrights & 4) != 0) { x += addDeviceAttribute("Tags", '<span onclick=showEditNodeValueDialog(3) style=line-height:26px;cursor:pointer;color:black>' + groupingTags + '</span>'); } else { x += addDeviceAttribute("Tags", '<span style=line-height:26px;color:black>' + groupingTags + '</span>'); } // SSH & RDP Credentials if ((node.ssh != null) || (node.rdp != null)) { var y = []; if ((meshrights & 4) != 0) { if (node.ssh != null) { y.push('<span onclick=showClearSshDialog(3) style=cursor:pointer>' + ((node.ssh == 2) ? "SSH-Key" : "SSH") + ' <img class=hoverButton src="images/link5.png" width=10 height=10 /></span>'); } if (node.rdp != null) { y.push('<span onclick=showClearRdpDialog(3) style=cursor:pointer>' + "RDP" + ' <img class=hoverButton src="images/link5.png" width=10 height=10 /></span>'); } } else { if (node.ssh != null) { y.push(((node.ssh == 2) ? "SSH-Key" : "SSH")); } if (node.rdp != null) { y.push("RDP"); } } x += addDeviceAttribute("Credentials", y.join(', ')); } x += '</table><br />'; // Show action button, only show if we have permissions 4, 8, 64 if (((meshrights & (4 + 8 + 64)) != 0) && (node.mtype != 3)) { x += '<input type=button value="' + "Actions" + '" onclick=deviceActionFunction() />'; } x += '<input type=button value=Notes onclick=showNotes(' + ((meshrights & 128) == 0) + ',"' + encodeURIComponent(node._id) + '") />'; //if ((connectivity & 1) && (meshrights & 8) && (node.agent.id < 5)) { x += '<input type=button value=Toast onclick=deviceToastFunction() />'; } QH('p10html', x); // If we are looking at a local non-windows device, enable terminal and files capability. if ((node.mtype == 3) && (node.agent != null) && (node.agent.id > 4) && (features2 & 0x00000200)) { node.agent.caps = 6; } // Show node last 7 days timeline //drawDeviceTimeline(); setupTerminal(); setupFiles(); if (meshrights & 16) { setupConsole(); } // Show bottom buttons x = '<div style=float:right;font-size:x-small;margin-right:10px>'; if ((meshrights & 0x8000) != 0) { x += '<a style=cursor:pointer onclick=p10showDeleteNodeDialog("' + node._id + '")>' + "Delete Device" + '</a>'; } x += '</div><div style=font-size:x-small>'; //if (mesh.mtype == 2) x += '<a style=cursor:pointer onclick=p10showNodeNetInfoDialog("' + node._id + '")>Interfaces</a> '; //if (xxmap != null) x += '<a style=cursor:pointer onclick=p10showNodeLocationDialog("' + node._id + '")>Location</a> '; x += '</div><br>' QH('p10html3', x); // Set the node power state var powerstate = PowerStateStr(node.state); //if (node.state == 0) { powerstate = 'Unknown State'; } if ((connectivity & 1) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += "Mesh Agent"; } if ((connectivity & 2) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += "Intel® AMT connected"; } else if ((connectivity & 4) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += "Intel® AMT detected"; } if ((connectivity & 16) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += "MQTT channel connected"; } if ((node.pwr > 1) && (node.pwr != 7)) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += powerStateStrings[node.pwr]; } QH('MainComputerState', '<span style=font-size:12px>' + powerstate + '</span>'); // Set the node icon QH('MainComputerImage', '<div class="i' + node.icon + '"></div>'); // Request the power timeline if ((powerTimelineNode != currentNode._id) && (powerTimelineReq != currentNode._id)) { QH('p10html2', ''); powerTimelineReq = currentNode._id; meshserver.send({ action: 'powertimeline', nodeid: currentNode._id }); meshserver.send({ action: 'lastconnect', nodeid: currentNode._id }); meshserver.send({ action: 'getsysinfo', nodeid: currentNode._id }); meshserver.send({ action: 'getnetworkinfo', nodeid: currentNode._id }); QH('p10detailshtml', ''); } // Clear user consent status if present if (deviceSwitch) { p11clearConsoleMsg(); p13clearConsoleMsg(); } // Clear the desktop session selector QV('p11DeskSessionSelector', false); QH('p11DeskSessionSelector', ''); } setupDesktop(); // Always refresh the desktop, even if we are on the same device, we need to do some canvas switching. if (!panel) panel = 10; go(panel); // Update the footer menu if (xxcurrentView == 10) { setupDeviceMenu(); } } function deviceToastFunction() { if (xxdialogMode) return; setDialogMode(2, "Device Toast", 3, deviceToastFunctionEx, '<textarea id=d2devToast style=width:100%;height:80px;resize:none;overflow-y:scroll></textarea>'); } function deviceToastFunctionEx() { meshserver.send({ action: 'toast', nodeids: [currentNode._id], title: 'MeshCentral', msg: Q('d2devToast').value }); } // && ((meshrights == 0xFFFFFFFF) || ((meshrights & 65536) == 0)) function setupDeviceMenu(op, obj) { var meshrights = GetNodeRights(currentNode); if (op != null) { currentDevicePanel = op; } QV('p10general', currentDevicePanel == 0); QV('p10desktop', currentDevicePanel == 1); // Show if we have remote control rights or desktop view only rights QV('p10files', currentDevicePanel == 2); QV('p10details', currentDevicePanel == 3); QV('p10console', currentDevicePanel == 4); QV('p10terminal', currentDevicePanel == 5); var menus = []; if (currentDevicePanel != 0) { menus.push({ n: "General", f: 'setupDeviceMenu(0)' }); } if ((currentDevicePanel != 1) && (currentNode != null) && ((meshrights & 8) || (meshrights & 256)) && ((meshrights == 0xFFFFFFFF) || ((meshrights & 65536) == 0)) && (((currentNode.agent == null) && ((typeof currentNode.intelamt.sku !== 'number') || ((currentNode.intelamt.sku & 8) != 0))) || (currentNode.agent && (currentNode.agent.caps & 1))) ) { menus.push({ n: "Desktop", f: 'setupDeviceMenu(1)' }); } if ((currentDevicePanel != 5) && (currentNode != null) && ((meshrights & 8) || (meshrights & 256)) && ((meshrights == 0xFFFFFFFF) || ((meshrights & 512) == 0)) && (((currentNode.agent == null) && ((typeof currentNode.intelamt.sku !== 'number') || ((currentNode.intelamt.sku & 8) != 0))) || (currentNode.agent && (currentNode.agent.caps & 2))) ) { menus.push({ n: "Terminal", f: 'setupDeviceMenu(5)' }); } if ((currentDevicePanel != 2) && (currentNode != null) && (meshrights & 8) && ((meshrights == 0xFFFFFFFF) || ((meshrights & 1024) == 0)) && ((currentNode.mtype != 1) && (currentNode.agent.caps & 4))) { menus.push({ n: "Files", f: 'setupDeviceMenu(2)' }); } if ((currentDevicePanel != 3) && (currentNode != null) && (currentNode.mtype != 3) && ((meshrights & 1048576) != 0)) { menus.push({ n: "Details", f: 'setupDeviceMenu(3)' }); } if ((currentDevicePanel != 4) && (currentNode != null) && (meshrights & 0x00000010) && (currentNode.mtype == 2)) { menus.push({ n: "Console", f: 'setupDeviceMenu(4)' }); } updateFooterMenu(menus); updateCurrentUrl(); if (currentDevicePanel == 1) { deskAdjust(); } } /* function deviceActionFunction() { if (xxdialogMode) return; var rights = GetNodeRights(currentNode), count = 0; var x = "Select an operation to perform on this device." + '<br /><br />'; var y = '<select id=d2deviceop style=float:right;width:170px>'; if ((rights & 64) != 0) { count++; y += '<option value=100>' + "Wake-up" + '</option>'; } // Wake-up permission //if (((currentNode.conn & 1) != 0) && ((rights & 131072) != 0)) { count++; y += '<option value=106>' + "Run Commands" + '</option>'; } // Remote command permission if ((currentNode.conn != 0) && ((rights & 262144) != 0)) { count++; y += '<option value=4>' + "Sleep" + '</option><option value=3>' + "Reset" + '</option><option value=2>' + "Power off" + '</option>'; } //if ((currentNode.conn & 16) != 0) { count++; y += '<option value=103>' + "Send MQTT Message" + '</option>'; } //if (((currentNode.conn & 1) != 0) && ((rights & 32768) != 0)) { count++; y += '<option value=104>' + "Uninstall Agent" + '</option>'; } y += '</select>'; x += addHtmlValue("Operation", y); if (count == 0) { x = "No actions currently available for this device."; } setDialogMode(2, "Device Action", (count == 0) ? 1 : 3, deviceActionFunctionEx, x); } function deviceActionFunctionEx() { var op = Q('d2deviceop').value; if (op == 100) { // Device wake meshserver.send({ action: 'wakedevices', nodeids: [currentNode._id] }); } else { // Power operation meshserver.send({ action: 'poweraction', nodeids: [currentNode._id], actiontype: op }); } } */ function deviceActionFunction() { if (xxdialogMode) return; var rights = GetNodeRights(currentNode), count = 0; var x = "Select an operation to perform on this device." + '<br /><br />'; var y = '<select id=d2deviceop style=float:right;width:170px onchange=deviceActionFunctionValidate()>'; var z = ''; if ((currentNode.agent != null) && (currentNode.agent.id == 14)) { if (((currentNode.conn & 1) != 0) && ((rights & 8) != 0)) { count++; y += '<option value=400>' + "Flash" + '</option>'; y += '<option value=401>' + "Vibrate" + '</option>'; z += '<div id=d2devicetimediv>' + addHtmlValue("Time", '<select id=d2devicetime style=float:right;width:170px><option value=1000>' + "1 second" + '</option><option value=5000>' + "5 seconds" + '</option><option value=10000>' + "10 seconds" + '</option></select>') + '</div>'; } } else { if ((rights & 64) != 0) { count++; y += '<option value=100>' + "Wake-up" + '</option>'; } // Wake-up permission //if (((currentNode.conn & 1) != 0) && ((rights & 131072) != 0)) { count++; y += '<option value=106>' + "Run Commands" + '</option>'; } // Remote command permission if ((currentNode.conn != 0) && ((rights & 262144) != 0)) { count++; y += '<option value=4>' + "Sleep" + '</option><option value=3>' + "Reset" + '</option><option value=2>' + "Power off" + '</option>'; } //if ((currentNode.conn & 16) != 0) { count++; y += '<option value=103>' + "Send MQTT Message" + '</option>'; } if ((currentNode.intelamt != null) && (currentNode.intelamt.state == 2) && ((currentNode.conn & 6) != 0) && (rights == 0xFFFFFFFF)) { count++; y += '<option value=310>' + "Intel® AMT Reset" + '</option>'; y += '<option value=302>' + "Intel® AMT Power on" + '</option>'; y += '<option value=308>' + "Intel® AMT Power off" + '</option>'; } //if ((getNodeAmtVersion(currentNode) >= 15) && (currentNode.intelamt.state == 2) && ((currentNode.conn & 6) != 0) && (rights == 0xFFFFFFFF) && ((features & 0x00000400) == 0)) { count++; y += '<option value=107>' + "Intel® AMT One Click Recovery" + '</option>'; } // CIRA (2) or AMT (4) connected //if (((currentNode.conn & 1) != 0) && ((rights & 32768) != 0)) { count++; y += '<option value=104>' + "Uninstall Agent" + '</option>'; } } y += '</select>'; x += addHtmlValue("Operation", y); if (count == 0) { x = "No actions currently available for this device."; } setDialogMode(2, "Device Action", (count == 0) ? 2 : 3, deviceActionFunctionEx, x + z); if (count > 0) { deviceActionFunctionValidate(); } } function deviceActionFunctionValidate() { var op = Q('d2deviceop').value; try { QV('d2devicetimediv', (op == 400) || (op == 401)); } catch (ex) { } } function deviceActionFunctionEx() { var op = Q('d2deviceop').value; if (op == 100) { // Device wake meshserver.send({ action: 'wakedevices', nodeids: [currentNode._id] }); } else if (op == 103) { // Send MQTT Message //p10showSendMqttMsgDialog([currentNode._id]); } else if (op == 104) { // Uninstall agent //p10showSendUninstallAgentDialog([currentNode._id]); } else if (op == 106) { // Run commands /* var wintype = false, linuxtype = false; if (currentNode.agent) { if ((currentNode.agent.id > 0) && (currentNode.agent.id < 5)) { wintype = true; } else { linuxtype = true; } } if ((wintype == true) || (linuxtype == true)) { var x = "Run commands on selected devices." + '<br />'; if (wintype == true) { x += '<select id=d2cmdtype style=width:100%;margin-bottom:4px;margin-top:4px>'; x += '<option value=1>' + "Windows Command Prompt" + '</option><option value=2>' + "Windows PowerShell" + '</option>'; if (linuxtype == true) { x += '<option value=3>' + "Linux/BSD/macOS Command Shell" + '</option>'; } x += '</select>'; } x += '<select id=d2cmduser style=width:100%;margin-bottom:4px><option value=0>' + "Run as agent" + '</option><option value=1>' + "Run as user, agent if no user" + '</option><option value=2>' + "Must run as user" + '</option></select>'; x += '<textarea id=d2runcmd style=background-color:#fcf3cf;width:100%;height:200px;resize:none;overflow-y:scroll></textarea>'; setDialogMode(2, "Run Commands", 3, deviceRunCmdsFunctionEx, x); Q('d2runcmd').focus(); //QE('idx_dlgOkButton', true); } */ } else if (op == 107) { // Intel AMT One Click Recovery (OCR) /* Q('d3localmodeform').action = 'oneclickrecovery.ashx'; Q('d3auth').value = authCookie; Q('d3filter').value = '.iso'; Q('d3attrib').value = currentNode._id; setDialogMode(3, "Intel® AMT One Click Recovery", 3, deviceActionOneClickRecovery); d3init(); */ } else if (op == 302) { // Intel AMT power on setDialogMode(2, "Intel® AMT Power Operation", 3, function () { meshserver.send({ action: 'poweraction', nodeids: [currentNode._id], actiontype: parseInt(op) }); }, "Perform Intel® AMT power on?"); } else if (op == 308) { // Intel AMT power off setDialogMode(2, "Intel® AMT Power Operation", 3, function () { meshserver.send({ action: 'poweraction', nodeids: [currentNode._id], actiontype: parseInt(op) }); }, "Perform Intel® AMT power off?"); } else if (op == 310) { // Intel AMT reset setDialogMode(2, "Intel® AMT Power Operation", 3, function () { meshserver.send({ action: 'poweraction', nodeids: [currentNode._id], actiontype: parseInt(op) }); }, "Perform Intel® AMT reset?"); } else if ((op == 400) || (op == 401)) { // Flash / vibrate meshserver.send({ action: 'poweraction', nodeids: [currentNode._id], actiontype: parseInt(op), time: parseInt(Q('d2devicetime').value) }); } else { // Power operation meshserver.send({ action: 'poweraction', nodeids: [currentNode._id], actiontype: parseInt(op) }); } } function showNotes(readonly, noteid) { if (xxdialogMode) return; if (noteid == null) { noteid = encodeURIComponentEx('p' + userinfo._id); } var x = '<textarea id=d2devNotes ro=' + readonly + ' noteid=' + noteid + ' readonly style=background-color:#fcf3cf;width:100%;height:200px;resize:none;overflow-y:scroll></textarea>'; if (noteid.startsWith('node%2F%2F')) { x += '<span style=font-size:10px>' + "Device group notes can be viewed and changed by other device group administrators." + '<span>'; } setDialogMode(2, "Notes", 3, showNotesEx, x, noteid); meshserver.send({ action: 'getNotes', id: decodeURIComponent(noteid) }); } function showNotesEx(buttons, tag) { meshserver.send({ action: 'setNotes', id: decodeURIComponent(tag), notes: encodeURIComponentEx(Q('d2devNotes').value) }); } function deviceChat(e) { if (xxdialogMode) return; setDialogMode(2, "Device Action", 3, function () { var url = '/messenger?id=meshmessenger/' + encodeURIComponentEx(currentNode._id) + '/' + encodeURIComponentEx(userinfo._id) + '&title=' + currentNode.name; if (serverinfo.domainsuffix != '') { url = '/' + serverinfo.domainsuffix + url; } if ((authCookie != null) && (authCookie != '')) { url += '&auth=' + authCookie; } if (e && (e.shiftKey == true)) { safeNewWindow(url, 'meshmessenger:' + currentNode._id); } else { safeNewWindow(url, 'meshmessenger:' + currentNode._id, 'directories=no,titlebar=no,toolbar=no,location=no,status=no,menubar=no,scrollbars=no,resizable=no,width=400,height=560'); } meshserver.send({ action: 'meshmessenger', nodeid: decodeURIComponent(currentNode._id) }); }, "Start chat session?"); } // Look to see if we need to update the device timeline function updateDeviceTimeline() { if ((meshserver.State != 2) || (powerTimelineNode == null) || (powerTimelineUpdate == null) || (currentNode == null) || (currentNode.mtype == 3)) return; if ((powerTimelineNode == powerTimelineReq) && (currentNode._id == powerTimelineNode) && (powerTimelineUpdate < Date.now())) { powerTimelineUpdate = null; meshserver.send({ action: 'powertimeline', nodeid: currentNode._id }); } } // Draw device power bars. The bars are 766px wide. function drawDeviceTimeline() { if (currentNode.mtype == 3) return; var timeline = null, now = Date.now(); if (currentNode._id == powerTimelineNode) { timeline = powerTimeline; } // Calculate when the timeline starts var d = new Date(); d.setHours(0, 0, 0, 0); d = new Date(d.getTime() - (1000 * 60 * 60 * 24 * 6)); var timelineStart = d.getTime(); // De-compact the timeline var timeline2 = []; if (timeline != null && timeline.length > 1) { timeline2.push([0, timeline[1], timeline[0]]); // Start, End, Power var ct = timeline[1]; for (var i = 2; i < timeline.length; i += 2) { var power = timeline[i], dt = now; if (timeline.length > (i + 1)) { dt = timeline[i + 1]; } timeline2.push([ct, ct + dt, power]); // Start, End, Power ct = ct + dt; } } // Draw the timeline var x = '', count = 1, date = new Date(); var totalWidth = Q('masthead').offsetWidth - (90 + 9 + 9 + 14); // Compute the total width of the power bar date.setHours(0, 0, 0, 0); for (var i = 0; i < 7; i++) { var datavalue = '', start = date.getTime(), end = start + (1000 * 60 * 60 * 24); for (var j in timeline2) { var block = timeline2[j]; if (isTimeBlockInside(start, end, block[0], block[1]) == true) { var ts = Math.max(start, block[0]); var te = Math.min(Math.min(end, block[1]), now); var width = Math.round(((te - ts) * totalWidth) / 86400000); if (width > 0) { datavalue += '<div style=display:table-cell;width:' + width + 'px;background-color:' + powerColor(block[2]) + ';height:16px></div>'; } } } x += '<tr style=' + (((count % 2) == 0) ? 'background-color:#DDD' : '') + '><td><div> ' + printDate(date) + '<div></div></div></td><td><div>' + datavalue + '</div></td></tr>'; ++count; date = new Date(date.getTime() - (1000 * 60 * 60 * 24)); // Substract one day } QH('p10html2', '<table style="color:black;background-color:#EEE;border-color:#AAA;border-width:1px;border-style:solid;border-collapse:collapse;width:calc(100% - 18px);margin:9px" border=0 cellpadding=2 cellspacing=0><tbody><tr style=background-color:#AAAAAA;font-weight:bold><th scope=col style=text-align:center;width:90px>' + "Day" + '</th><th scope=col style=text-align:center>' + "Power State" + '</th></tr>' + x + '</tbody></table>'); } // Return a color for the given power state function powerColor(x) { if (x < powerColorTable.length) { return powerColorTable[x]; } return 'yellow'; } // Return true if the time block is visible within the start/end period function isTimeBlockInside(start, end, blockStart, blockEnd) { if ((blockStart < start) && (blockEnd > end)) return true; // Block is wider than timespan if ((blockStart > start) && (blockStart < end)) return true; if ((blockEnd > start) && (blockEnd < end)) return true; return false; } function addDeviceAttribute(name, value) { return '<tr><td style=width:100px;color:gray>' + name + '</td><td style=overflow:hidden>' + value + '</td></tr>'; } function editDeviceAmtSettings(nodeid, func) { if (xxdialogMode) return; var x = '', node = getNodeFromId(nodeid), buttons = 3, meshrights = GetNodeRights(node); if ((meshrights & 4) == 0) return; x += addHtmlValue("Username", '<input id=dp10username style=width:170px maxlength=32 autocomplete=nope placeholder="admin" onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />'); x += addHtmlValue("Password", '<input id=dp10password type=password style=width:170px autocomplete=nope maxlength=32 onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />'); // Only display the TLS setting if the Intel AMT manager is not running on the server. With the manager TLS is auto-detected. if ((features2 & 1) == 0) { x += addHtmlValue("Security", '<select id=dp10tls style=width:176px><option value=0>' + "No TLS security" + '</option><option value=1>' + "TLS security required" + '</option></select>'); } if ((node.intelamt.user != null) && (node.intelamt.user != '')) { buttons = 7; } setDialogMode(2, "Edit Intel® AMT credentials", buttons, editDeviceAmtSettingsEx, x, { node: node, func: func }); if ((node.intelamt.user != null) && (node.intelamt.user != '')) { Q('dp10username').value = node.intelamt.user; } else { Q('dp10username').value = 'admin'; } if ((features2 & 1) == 0) { Q('dp10tls').value = node.intelamt.tls; } validateDeviceAmtSettings(); } function validateDeviceAmtSettings() { QE('idx_dlgOkButton', passwordcheck(Q('dp10password').value)); } function editDeviceAmtSettingsEx(button, tag) { if (button == 2) { // Delete button pressed, remove credentials meshserver.send({ action: 'changedevice', nodeid: tag.node._id, intelamt: { user: '', pass: '' } }); } else { // Change Intel AMT credentials var amtuser = Q('dp10username').value; if (amtuser == '') amtuser = 'admin'; var amtpass = Q('dp10password').value; if (amtpass == '') amtuser = ''; var x = { action: 'changedevice', nodeid: tag.node._id, intelamt: { user: amtuser, pass: amtpass } }; if ((features2 & 1) == 0) { x.intelamt.tls = parseInt(Q('dp10tls').value); } meshserver.send(x); if (tag.func) { setTimeout(tag.func, 1000); } } } function p10showDeleteNodeDialog(nodeid) { if (xxdialogMode) return; setDialogMode(2, "Delete Node", 3, p10showDeleteNodeDialogEx, format("Delete {0}?", EscapeHtml(currentNode.name)) + '<br /><br /><label><input id=p10check type=checkbox onchange=p10validateDeleteNodeDialog() />' + "Confirm" + '</label>', nodeid); p10validateDeleteNodeDialog(); } function p10validateDeleteNodeDialog() { QE('idx_dlgOkButton', Q('p10check').checked); } function p10showDeleteNodeDialogEx(buttons, nodeid) { meshserver.send({ action: 'removedevices', nodeids: [nodeid] }); } function p10showiconselector() { if (xxdialogMode) return; var rights = GetNodeRights(currentNode); if ((rights & 4) == 0) return; var x = '<table align=center><td style=text-align:center>'; x += '<div style=display:inline-block class=i1 onclick=p10setIcon(1)></div>'; x += '<div style=display:inline-block class=i2 onclick=p10setIcon(2)></div>'; x += '<div style=display:inline-block class=i3 onclick=p10setIcon(3)></div>'; x += '<div style=display:inline-block class=i4 onclick=p10setIcon(4)></div><br />'; x += '<div style=display:inline-block class=i5 onclick=p10setIcon(5)></div>'; x += '<div style=display:inline-block class=i6 onclick=p10setIcon(6)></div>'; x += '<div style=display:inline-block class=i7 onclick=p10setIcon(7)></div>'; x += '<div style=display:inline-block class=i8 onclick=p10setIcon(8)></div></table>'; setDialogMode(2, "Icon Selection", 0, null, x); QV('id_dialogclose', true); } function p10setIcon(icon) { setDialogMode(0); meshserver.send({ action: 'changedevice', nodeid: currentNode._id, icon: icon }); } function showClearSshDialog() { setDialogMode(2, "Edit Device", 3, showClearSshDialogEx, "Clear SSH credentials?"); } function showClearSshDialogEx(button, mode) { meshserver.send({ action: 'changedevice', nodeid: currentNode._id, ssh: 0 }); } function showClearRdpDialog() { setDialogMode(2, "Edit Device", 3, showClearRdpDialogEx, "Clear RDP credentials?"); } function showClearRdpDialogEx(button, mode) { meshserver.send({ action: 'changedevice', nodeid: currentNode._id, rdp: 0 }); } var showEditNodeValueDialog_modes = ["Device Name", "Hostname", "Description", "Tags"]; var showEditNodeValueDialog_modes2 = ['name', 'host', 'desc', 'tags']; var showEditNodeValueDialog_modes3 = ['', '', '', "Group1, Group2, Group3"]; function showEditNodeValueDialog(mode) { if (xxdialogMode) return; var x = addHtmlValue(showEditNodeValueDialog_modes[mode], '<input id=dp10devicevalue style=width:170px maxlength=64 placeholder="' + showEditNodeValueDialog_modes3[mode] + '" onchange=p10editdevicevalueValidate(' + mode + ',event) onkeyup=p10editdevicevalueValidate(' + mode + ',event) />'); if (mode == 3) { // Get a list of all possible device tags var allTags = [], y = ''; for (var i in nodes) { if (nodes[i].tags) { for (var j in nodes[i].tags) { if (allTags.indexOf(nodes[i].tags[j]) == -1) { allTags.push(nodes[i].tags[j]); } } } } if (allTags.length > 0) { allTags.sort(); for (var i in allTags) { y += '<span style=padding:4px;background-color:#BBB;border-radius:3px;cursor:pointer onclick=showEditNodeValueDialogAddTag("' + encodeURIComponentEx(allTags[i]) + '")>' + EscapeHtml(allTags[i]) + '</span> '; } x += '<div style=margin-top:8px;width:280px;line-height:26px;max-height:160px;overflow-y:auto>' + y + '</div>'; } } setDialogMode(2, "Edit Device", 3, showEditNodeValueDialogEx, x, mode); var v = currentNode[showEditNodeValueDialog_modes2[mode]]; if (v == null) v = ''; if (Array.isArray(v)) { v = v.join(', '); } Q('dp10devicevalue').value = v; p10editdevicevalueValidate(); Q('dp10devicevalue').focus(); } function showEditNodeValueDialogAddTag(t) { var tt = Q('dp10devicevalue').value.split(','), t2 = []; for (var i in tt) { t2.push(tt[i].trim()); } if (t2.indexOf(t) >= 0) return; Q('dp10devicevalue').value += ((Q('dp10devicevalue').value.length == 0) ? '' : ', ') + decodeURIComponent(t); setTimeout(function () { Q('dp10devicevalue').selectionStart = Q('dp10devicevalue').selectionEnd = 90000; }, 0); p10editdevicevalueValidate(); } function showEditNodeValueDialogEx(button, mode) { var x = { action: 'changedevice', nodeid: currentNode._id }; x[showEditNodeValueDialog_modes2[mode]] = Q('dp10devicevalue').value; meshserver.send(x); } function p10editdevicevalueValidate(mode, e) { var x = ((mode > 1) || (Q('dp10devicevalue').value.length > 0)); QE('idx_dlgOkButton', x); if ((e != null) && (x == true) && (e.keyCode == 13)) { dialogclose(1); } } // // DESKTOP // var desktop; var desktopNode; var desktopsettings = { encoding: 2, showfocus: false, showmouse: true, showcad: true, quality: 40, scaling: 1024, framerate: 50, autolock: false }; function setupDesktop() { // Setup the remote desktop if ((desktopNode != currentNode) && (desktop != null)) { desktop.Stop(); desktopNode = null; desktop = null; } // If the device desktop is already connected in multi-desktop, use that. if ((desktopNode != currentNode) || (desktop == null)) { // Device is not already connected, just setup a blank canvas //QH('DeskParent', '<canvas id=Desk width=640 height=200 style="width:100%;-ms-touch-action:none;margin-left:0px" oncontextmenu="return false" onmousedown=dmousedown(event) onmouseup=dmouseup(event) onmousemove=dmousemove(event)></canvas>'); desktopNode = currentNode; // Setup the mouse wheel Q('Desk').addEventListener('DOMMouseScroll', function (e) { return dmousewheel(e); }); Q('Desk').addEventListener('mousewheel', function (e) { return dmousewheel(e); }); } desktopNode = currentNode; updateDesktopButtons(); // On some browsers like IE, we can't save screen shots. Hide the scheenshot/capture buttons. if (!Q('Desk')['toBlob']) { QV('deskSaveBtn', false); } } // Show and enable the right buttons function updateDesktopButtons() { var mesh = meshes[currentNode.meshid]; var deskState = 0; if (desktop != null) { deskState = desktop.State; } var meshrights = GetNodeRights(currentNode); // Show the right buttons QV('disconnectbutton1', (deskState != 0)); QE('deskFullScreen', (deskState != 0)); QV('connectbutton1', (deskState == 0) && ((meshrights & 8) || (meshrights & 256)) && (currentNode.agent != null) && (currentNode.agent.caps & 1)); QV('connectbutton1h', (deskState == 0) && (meshrights & 8) && ( ((currentNode.intelamt != null) && (currentNode.intelamt.state == 2) && (currentNode.intelamt.ver != null) && ((currentNode.intelamt.sku == null) || ((typeof currentNode.intelamt.sku == 'number') && ((currentNode.intelamt.sku & 8) != 0)))) ) ); // Show the right settings QV('d7amtkvm', (currentNode.intelamt != null && ((currentNode.intelamt.ver != null) || (currentNode.agent == null))) && ((deskState == 0) || (desktop.contype == 2))); QV('d7meshkvm', ((currentNode.agent != null) && (currentNode.agent.caps & 1) && ((deskState == false) || (desktop.contype == 1)))); // Enable buttons var online = ((currentNode.conn & 1) != 0); // If Agent (1) connected, enable remote desktop QE('connectbutton1', online); var hwonline = ((currentNode.conn & 6) != 0); // If CIRA (2) or AMT (4) connected, enable hardware terminal QE('connectbutton1h', hwonline); //QE('deskSaveBtn', deskState == 3); //QV('DeskCAD', meshrights & 8); //QE('DeskCAD', deskState == 3); //QV('DeskWD', (currentNode.agent) && (currentNode.agent.id < 5)); //QE('DeskWD', deskState == 3); //QV('deskkeys', (currentNode.agent) && (currentNode.agent.id < 5)); //QE('deskkeys', deskState == 3); //QE('DeskToolsButton', online); QV('DeskToastButton', ((meshrights & 16384) != 0) && (currentNode.agent) && (currentNode.agent.id < 5) && (meshrights & 8)); //QE('DeskToastButton', online); QV('deskActionsBtn', meshrights & 8); Q('DeskControl').checked = ((meshrights & 8) != 0); if (online == false) QV('DeskTools', false); } // Used to translate incoming agent console messages var agentConsoleMessages = ['', "Waiting for user to grant access...", "Denied", "Failed to start remote terminal session, {0} ({1})", "Timeout", "Received invalid network data"]; function formatAgentConsoleMessage(msg, msgid, msgargs) { var r; if (msgargs == null) { msgargs = []; } while (msgargs.length < 3) { msgargs.push(''); } // We need to call the format function in a way that works with older browsers and minifier, can't use apply() or ... if (msgid && (msgid < agentConsoleMessages.length)) { r = EscapeHtml(format(agentConsoleMessages[msgid], (msgargs[0]), (msgargs[1]), (msgargs[2]))); } else { r = EscapeHtml(msg); } return r.split('\n').join('<br />') + '<br /><br />'; } function connectDesktop(e, contype, tsid, consent) { setSessionActivity(); QV('p11DeskSessionSelector', false); p11clearConsoleMsg(); if (desktop == null) { desktopNode = currentNode; if (contype == 2) { // Setup the Intel AMT remote desktop if ((desktopNode.intelamt.user == null) || (desktopNode.intelamt.user == '')) { editDeviceAmtSettings(desktopNode._id, connectDesktop); return; } desktop = CreateAmtRedirect(CreateAmtRemoteDesktop('Desk'), authCookie); desktop.debugmode = debugmode; desktop.onStateChanged = onDesktopStateChange; desktop.m.bpp = (desktopsettings.encoding == 1 || desktopsettings.encoding == 3) ? 1 : 2; desktop.m.useZRLE = (desktopsettings.encoding < 3); desktop.m.showmouse = true; desktop.m.onScreenSizeChange = function (o, x, y) { if (fullscreen) { QS('deskarea3').width = (x * fullscreenzoom) + 'px'; QS('deskarea3').height = (y * fullscreenzoom) + 'px'; } deskAdjust(); } desktop.Start(desktopNode._id, 16994, '*', '*', 0); desktop.contype = 2; } else if ((contype == null) || (contype == 1) || ((contype == 3) && (currentNode.agent.id > 4))) { // Setup the Mesh Agent remote desktop desktop = CreateAgentRedirect(meshserver, CreateAgentRemoteDesktop('Desk'), serverPublicNamePort, authCookie, authRelayCookie, domainUrl); desktop.debugmode = debugmode; desktop.m.debugmode = debugmode; desktop.attemptWebRTC = attemptWebRTC; desktop.options = {}; if (tsid != null) { desktop.options.tsid = tsid; } if (consent != null) { desktop.options.consent = consent; } if (desktopsettings.autolock == true) { desktop.options.autolock = true; } desktop.onStateChanged = onDesktopStateChange; if ((features2 & 0x2000) != 0) desktop.m.stopInput = true; desktop.onConsoleMessageChange = function () { if (desktop.consoleMessage) { Q('p11DeskConsoleMsg').innerHTML += formatAgentConsoleMessage(desktop.consoleMessage, desktop.consoleMessageId, desktop.consoleMessageArgs); QV('p11DeskConsoleMsg', true); if (p11DeskConsoleMsgTimer != null) { clearTimeout(p11DeskConsoleMsgTimer); } if (desktop.consoleMessageTimeout) { p11DeskConsoleMsgTimer = setTimeout(p11clearConsoleMsg, desktop.consoleMessageTimeout * 1000); } } else { p11clearConsoleMsg(); } } desktop.m.ImageType = webpSupport ? 4 : 1; // Send 4 if WebP is supported, otherwise send 1 for JPEG. desktop.m.CompressionLevel = desktopsettings.quality; // Number from 1 to 100. 50 or less is best. desktop.m.ScalingLevel = desktopsettings.scaling; desktop.m.FrameRateTimer = desktopsettings.framerate; desktop.m.onDisplayinfo = deskDisplayInfo; desktop.m.onScreenSizeChange = function (o, x, y) { if (fullscreen) { QS('deskarea3').width = (x * fullscreenzoom) + 'px'; QS('deskarea3').height = (y * fullscreenzoom) + 'px'; } deskAdjust(); } desktop.Start(desktopNode._id); desktop.contype = 1; } else if (contype == 3) { // Ask for user sessions meshserver.send({ action: 'msg', type: 'userSessions', nodeid: currentNode._id, tag: consent }); } } else { // Disconnect and clean up the remote desktop desktop.Stop(); desktopNode = desktop = null; } } function p11clearConsoleMsg() { QH('p11DeskConsoleMsg', ''); QV('p11DeskConsoleMsg', false); if (p11DeskConsoleMsgTimer) { clearTimeout(p11DeskConsoleMsgTimer); p11DeskConsoleMsgTimer = null; } } function p12clearConsoleMsg() { QH('p12TermConsoleMsg', ''); QV('p12TermConsoleMsg', false); if (p12TermConsoleMsgTimer) { clearTimeout(p12TermConsoleMsgTimer); p12TermConsoleMsgTimer = null; } } function p13clearConsoleMsg() { QH('p13FilesConsoleMsg', ''); QV('p13FilesConsoleMsg', false); if (p13FilesConsoleMsgTimer) { clearTimeout(p13FilesConsoleMsgTimer); p13FilesConsoleMsgTimer = null; } } function p12setConsoleMsg(msg, timeout) { if (msg) { Q('p12TermConsoleMsg').innerHTML += msg; QV('p12TermConsoleMsg', true); if (p12TermConsoleMsgTimer != null) { clearTimeout(p12TermConsoleMsgTimer); } if (timeout) { p12TermConsoleMsgTimer = setTimeout(p12clearConsoleMsg, timeout); } } else { p12clearConsoleMsg(); } } function p13setConsoleMsg(msg, timeout) { if (msg) { Q('p13FilesConsoleMsg').innerHTML += msg; QV('p13FilesConsoleMsg', true); if (p13FilesConsoleMsgTimer != null) { clearTimeout(p13FilesConsoleMsgTimer); } if (timeout) { p13FilesConsoleMsgTimer = setTimeout(p13clearConsoleMsg, timeout); } } else { p13clearConsoleMsg(); } } function onDesktopStateChange(xdesktop, state) { var xstate = state; if ((xstate == 3) && (xdesktop.contype == 2)) { xstate++; } var str = StatusStrs[xstate]; if ((desktop != null) && (desktop.webRtcActive == true)) { str += ", WebRTC"; } //if (desktop.m.stopInput == true) { str += ', Loopback'; } QH('deskstatus', str); switch (state) { case 0: // Disconnect and clean up the remote desktop desktop.Stop(); desktopNode = desktop = null; QV('DeskScreens', false); if (fullscreen == true) { deskToggleFull(); } break; case 2: break; default: //console.log('Unknown onDesktopStateChange state', state); break; } updateDesktopButtons(); deskAdjust(); setTimeout(deskAdjust, 50); } function showDesktopSettings() { if (xxdialogMode) return; applyDesktopSettings(); updateDesktopButtons(); setDialogMode(7, "Remote Desktop Settings", 3, showDesktopSettingsChanged); } function showDesktopSettingsChanged() { desktopsettings.encoding = d7desktopmode.value; desktopsettings.quality = d7bitmapquality.value; desktopsettings.scaling = d7bitmapscaling.value; desktopsettings.framerate = d7framelimiter.value; desktopsettings.autolock = d7deskAutoLock.checked; localStorage.setItem('desktopsettings', JSON.stringify(desktopsettings)); applyDesktopSettings(); if (desktop) { if (desktop.contype == 1) { if (desktop.State != 0) { desktop.m.SendCompressionLevel(webpSupport ? 4 : 1, desktopsettings.quality, desktopsettings.scaling, desktopsettings.framerate); } desktop.sendCtrlMsg('{"ctrlChannel":"102938","type":"autolock","value":' + desktopsettings.autolock + '}'); } if (desktop.contype == 2) { if (desktop.State != 0) { desktop.Stop(); setTimeout(function () { connectDesktop(null, 2); }, 50); } } } } function applyDesktopSettings() { var r = '', ops = (features & 512) ? [90, 70, 50, 40, 30, 20, 10, 5, 1] : [50, 40, 30, 20, 10, 5, 1]; for (var i in ops) { r += '<option value=' + ops[i] + '>' + ops[i] + '%</option>'; } QH('d7bitmapquality', r); d7desktopmode.value = desktopsettings.encoding; d7bitmapquality.value = 40; // Default value if (ops.indexOf(parseInt(desktopsettings.quality)) >= 0) { d7bitmapquality.value = desktopsettings.quality; } d7bitmapscaling.value = desktopsettings.scaling; if (desktopsettings.framerate) { d7framelimiter.value = desktopsettings.framerate; } if (desktopsettings.autolock != null) { d7deskAutoLock.checked = desktopsettings.autolock; } } var keyboardShown = false; var keyboardShownTimer = null; var fullScreenMode = false; function toggleKeyboard() { if (xxdialogMode) return; if (keyboardShownTimer != null) { clearTimeout(keyboardShownTimer); } if (keyboardShown) { Q('softKeyboard').blur(); keyboardShown = false; } else { Q('softKeyboard').focus(); keyboardShown = true; } QV('deskkeybutton2a', fullscreen && !keyboardShown); QV('deskkeybutton2b', fullscreen && keyboardShown); } function keyboardFocusChange() { keyboardShownTimer = setTimeout(function () { keyboardShownTimer = null; keyboardShown = (Q('softKeyboard') == document.activeElement); QV('deskkeybutton2a', fullscreen && !keyboardShown); QV('deskkeybutton2b', fullscreen && keyboardShown); }, 10); } function exitButton() { if (xxdialogMode) return; QV('deskButtonMenu', false); QV('termButtonMenu', false); deskToggleFull(); } function deskMenuButton(x) { toggleMenu(true); deskSendKeys(x); } // // Desktop Shortcut Keys // function updateDeskShortcutKeys() { var x = '<div class="menuButton" onclick="deskMenuButton(-1)">' + "Customize" + '</div>'; for (var i in deskKeyboardShortcuts) { x += '<div class="menuButton" onclick="deskMenuButton(' + deskKeyboardShortcuts[i] + ')">' + keyShortcutTotext(deskKeyboardShortcuts[i]) + '</div>'; } QH('deskButtonMenu', x); } var keyStrings = { 8: "BackSpace", 9: "Tab", 13: "Enter", 27: "Escape", 44 : "Print Screen", 45: "Insert", 46: "Del", 36: "Home", 35: "End", 33: "Page Up", 34: "Page Down", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 0: "None" } function keyShortcutTotext(n) { var x = []; if (n & 0x010000) { x.push("Shift"); } if (n & 0x020000) { x.push("Alt"); } if (n & 0x080000) { x.push("Ctrl"); } if (n & 0x100000) { x.push("Win"); } n = (n & 0xFFFF); if ((n >= 112) && (n <= 123)) { x.push('F' + (n - 111)); } // Fx keys else if ((n != 0) && (keyStrings[n])) { x.push(keyStrings[n]); } else { if (n != 0) { x.push(String.fromCharCode(n)); } } return x.join(' + '); } // Customize keyboard shortcuts function deskCustomizeKeys() { if (xxdialogMode) return; var x = '<div id=d2shortcuts style="width:100%;height:180px;padding:4px;overflow-y:auto;border:1px solid gray"></div><div style=width:100%;padding:5px>'; x += '<label><input id=d1kshift type=checkbox /> ' + "Shift" + '</label><label> <input id=d1kalt type=checkbox /> ' + "Alt" + '</label><label> <input id=d1kctrl type=checkbox /> ' + "Ctrl" + '</label> <input id=d1kwin type=checkbox /> ' + "Win" + '</label>'; x += ' <select id=d2keySelect>'; for (var i in keyStrings) { x += '<option value=' + i + '>' + keyStrings[i] + '</option>'; } for (var i = 1; i <= 12; i++) { x += '<option value=' + (i + 111) + '>F' + i + '</option>'; } for (var i = 0; i < 10; i++) { x += '<option value=' + (i + 48) + '>' + i + '</option>'; } for (var i = 0; i < 26; i++) { x += '<option value=' + (i + 65) + '>' + String.fromCharCode(i + 65) + '</option>'; } x += '</select> <input type=button value=' + "Add" + ' onclick=addDeskCustomizeKey() /></div>'; QH('p10dialog2', x); xxdialogMode = 2; QV('p10dialog', true); deskUpdateShortcutList(); } function deskCustomizeKeysEx() { QV('p10dialog', false); xxdialogMode = 0; putstore('deskKeyShortcuts', deskKeyboardShortcuts.join(',')); updateDeskShortcutKeys(); } function deskUpdateShortcutList() { var x = ''; for (var i in deskKeyboardShortcuts) { var kt = keyShortcutTotext(deskKeyboardShortcuts[i]), orderButtons = ''; if (i != (deskKeyboardShortcuts.length - 1)) { orderButtons += '<img width=8 height=8 style=float:right;cursor:pointer;padding:3px src="images/c2.png" onclick=deskCustomizeKeyDown(' + deskKeyboardShortcuts[i] + ')>'; } if (i != 0) { orderButtons += '<img width=8 height=8 style=float:right;cursor:pointer;padding:3px src="images/c3.png" onclick=deskCustomizeKeyUp(' + deskKeyboardShortcuts[i] + ')>'; } x += '<div style="width:100%;background-color:#AAA;border-radius:4px;margin-bottom:4px;padding:4px;text-align:left;box-sizing:border-box" value=' + deskKeyboardShortcuts[i] + '>' + kt + '<img width=10 height=10 style=float:right;cursor:pointer;padding:2px;margin-left:8px src="images/trash.png" onclick=removeDeskCustomizeKey(' + deskKeyboardShortcuts[i] + ')>' + orderButtons + '</div>'; } if (x == '') { x = '<i>' + "No keyboard shortcuts defined" + '</i>'; } QH('d2shortcuts', x); } function deskCustomizeKeyDown(k) { var i = deskKeyboardShortcuts.indexOf(k), x = deskKeyboardShortcuts[i + 1]; deskKeyboardShortcuts[i + 1] = deskKeyboardShortcuts[i]; deskKeyboardShortcuts[i] = x; deskUpdateShortcutList(); } function deskCustomizeKeyUp(k) { var i = deskKeyboardShortcuts.indexOf(k), x = deskKeyboardShortcuts[i]; deskKeyboardShortcuts[i] = deskKeyboardShortcuts[i - 1]; deskKeyboardShortcuts[i - 1] = x; deskUpdateShortcutList(); } function removeDeskCustomizeKey(k) { var na = []; for (var i in deskKeyboardShortcuts) { if (deskKeyboardShortcuts[i] != k) { na.push(deskKeyboardShortcuts[i]); } } deskKeyboardShortcuts = na; deskUpdateShortcutList(); } function addDeskCustomizeKey() { var k = parseInt(Q('d2keySelect').value); if (Q('d1kshift').checked) { k |= 0x010000; } if (Q('d1kalt').checked) { k |= 0x020000; } if (Q('d1kctrl').checked) { k |= 0x080000; } if (Q('d1kwin').checked) { k |= 0x100000; } if ((k > 0) && (deskKeyboardShortcuts.indexOf(k) == -1)) { deskKeyboardShortcuts.push(k); deskUpdateShortcutList(); } } // Remote desktop special key combos for Windows function deskSendKeys(ks) { if (xxdialogMode || desktop == null || desktop.State != 3) return; // Construct the key command if (ks == -1) { deskCustomizeKeys(); return; } // Customize if (ks == 0x0A002E) { desktop.m.sendcad(); return; } // CTRL-ALT-DEL //if ((desktop.contype == 1) && (ks == 0x10004C)) { desktop.sendCtrlMsg('{"action":"lock"}'); return; } // Lock desktop, WIN + L var flags = (ks & 0xFF0000) >> 16, key = (ks & 0xFFFF), keyArray = [], keyArray2 = []; var amtTranslate = { 8: 0xff08, // BackSpace 9: 0xff09, // Tab 13: 0xff0d, // Return or Enter 27: 0xff1b, // Escape 45: 0xff63, // Insert 46: 0xffff, // Delete 36: 0xff50, // Home 35: 0xff57, // End 33: 0xff55, // Page Up 34: 0xff56, // Page Down 37: 0xff51, // Left arrow 38: 0xff52, // Up arrow 39: 0xff53, // Right arrow 40: 0xff54, // Down arrow 112: 0xffbe, // F1 113: 0xffbf, // F2 114: 0xffc0, // F3 115: 0xffc1, // F4 116: 0xffc2, // F5 117: 0xffc3, // F6 118: 0xffc4, // F7 119: 0xffc5, // F8 120: 0xffc6, // F9 121: 0xffc7, // F10 122: 0xffc8, // F11 123: 0xffc9 // F12 } // 0x010000 = Shift // 0x020000 = Left-Alt // 0x080000 = Ctrl // 0x100000 = Window if (desktop.contype == 2) { // Intel AMT if (flags & 1) { keyArray.push([0xffe1, 1]); keyArray2.push([0xffe1, 0]); } // Shift if (flags & 2) { keyArray.push([0xffe9, 1]); keyArray2.push([0xffe9, 0]); } // Left-alt if (flags & 8) { keyArray.push([0xffe3, 1]); keyArray2.push([0xffe3, 0]); } // Ctrl if (flags & 16) { keyArray.push([0xffe7, 1]); keyArray2.push([0xffe7, 0]); } // Windows key if (amtTranslate[key]) { key = amtTranslate[key]; } if ((key >= 65) && (key <= 90)) { key += 32; } if (key != 0) { keyArray.push([key, 1]); keyArray2.push([key, 0]); } keyArray2.reverse(); for (var i = 0; i < keyArray2.length; i++) { keyArray.push(keyArray2[i]); } desktop.m.sendkey(keyArray); } else { // Agent desktop if (flags & 1) { keyArray.push([desktop.m.KeyAction.DOWN, 16]); keyArray2.push([desktop.m.KeyAction.UP, 16]); } // Shift if (flags & 2) { keyArray.push([desktop.m.KeyAction.EXDOWN, 18]); keyArray2.push([desktop.m.KeyAction.EXUP, 18]); } // Left-alt if (flags & 8) { keyArray.push([desktop.m.KeyAction.EXDOWN, 17]); keyArray2.push([desktop.m.KeyAction.EXUP, 17]); } // Ctrl if (flags & 16) { keyArray.push([desktop.m.KeyAction.EXDOWN, 0x5B]); keyArray2.push([desktop.m.KeyAction.EXUP, 0x5B]); } // Windows key if (key != 0) { keyArray.push([desktop.m.KeyAction.DOWN, key]); keyArray2.push([desktop.m.KeyAction.UP, key]); } keyArray2.reverse(); for (var i = 0; i < keyArray2.length; i++) { keyArray.push(keyArray2[i]); } desktop.m.SendKeyMsgKC(keyArray); } } function toggleMenu(x) { if (xxdialogMode) return; QV('deskButtonMenu', fullscreen && !x && (currentDevicePanel == 1)); QV('termButtonMenu', fullscreen && !x && (currentDevicePanel == 5)); QV('deskkeybutton3a', fullscreen && x); QV('deskkeybutton3b', fullscreen && !x); } function deskChangeMouseButton(x) { if (xxdialogMode) return; if (desktop == null) return; desktop.m.SwapMouse = !desktop.m.SwapMouse; QV('deskkeybutton4a', fullscreen && (!desktop.m.SwapMouse)); QV('deskkeybutton4b', fullscreen && (desktop.m.SwapMouse)); } function deskChangeFullscreenZoom() { if (xxdialogMode) return; if (currentDevicePanel == 1) { if (desktop == null) return; if (fullscreenzoom == 1) { fullscreenzoom = 0.5; } else { fullscreenzoom = 1; } QV('deskkeybutton5a', fullscreen && (fullscreenzoom == 1)); QV('deskkeybutton5b', fullscreen && (fullscreenzoom != 1)); QS('deskarea3').width = (desktop.m.ScreenWidth * fullscreenzoom) + 'px'; QS('deskarea3').height = (desktop.m.ScreenHeight * fullscreenzoom) + 'px'; deskAdjust(); } if (currentDevicePanel == 5) { if (terminal == null) return; xterm.setOption('fontSize', (xterm.getOption('fontSize') == 15) ? 10 : 15) } } var fullscreen = false; var fullscreenzoom = 1; function deskToggleFull() { fullscreen = !fullscreen; QV('mastheadx', !fullscreen); QV('masthead', !fullscreen); QV('topbar', !fullscreen); QV('p11deviceNameHeader', !fullscreen); QV('footer', !fullscreen); QV('column_l_bottomgap', !fullscreen); QV('idx_deskFullBtn2', fullscreen); QV('deskFullBtn', !fullscreen); QV('p10deskTopTable', !fullscreen); QV('deskarea1', !fullscreen); QV('deskarea4', !fullscreen); QV('termarea1', !fullscreen); QV('termarea4', !fullscreen); var rights = GetNodeRights(currentNode); var inputAllowed = ((features2 & 0x2000) == 0) && (currentNode.agent.id != 14) && ((rights == 0xFFFFFFFF) || (((rights & 8) != 0) && ((rights & 256) == 0) && ((rights & 4096) == 0))); // Show full screen buttons if needed QV('deskkeybutton1', fullscreen); if (currentDevicePanel == 1) { // Desktop panel is being shown (1 = Desktop, 5 = Terminal) // Move shortcut key to desktop position QS('deskkeybutton2a').top = QS('deskkeybutton2b').top = '210px'; // Move the zoom button to normal or top position QS('deskkeybutton5a').top = QS('deskkeybutton5b').top = (inputAllowed) ? '160px' : '60px'; // Zoom QV('deskkeybutton2a', fullscreen && inputAllowed); QV('deskkeybutton2b', false); QV('deskkeybutton3a', fullscreen && inputAllowed); QV('deskkeybutton3b', false); QV('deskkeybutton4a', fullscreen && inputAllowed && (!desktop.m.SwapMouse)); QV('deskkeybutton4b', fullscreen && inputAllowed && (desktop.m.SwapMouse)); QV('deskkeybutton5a', fullscreen && (fullscreenzoom == 1)); QV('deskkeybutton5b', fullscreen && (fullscreenzoom != 1)); } if (currentDevicePanel == 5) { // Move right buttons to terminal position //QS('deskkeybutton3a').top = QS('deskkeybutton3b').top = '60px'; // Shortcuts //QS('deskkeybutton5a').top = QS('deskkeybutton5b').top = '110px'; // Zoom QS('deskkeybutton2a').top = QS('deskkeybutton2b').top = '110px'; // Keyboard QV('deskkeybutton2a', fullscreen); QV('deskkeybutton2b', false); QV('deskkeybutton3a', fullscreen); QV('deskkeybutton3b', false); QV('deskkeybutton4a', false); QV('deskkeybutton4b', false); QV('deskkeybutton5a', false); QV('deskkeybutton5a', false); //QV('deskkeybutton5a', xterm.getOption('fontSize') == 15); //QV('deskkeybutton5b', xterm.getOption('fontSize') != 15); } if (fullscreen) { QS('DeskParent').height = null; QS('page_content').top = '0px'; QS('page_content').bottom = '0px'; if (currentDevicePanel == 1) { QS('p10desktop').top = '0px'; QS('p10desktop').overflow = 'scroll'; QS('deskarea3').top = '0px'; QS('deskarea3').width = (desktop.m.ScreenWidth * fullscreenzoom) + 'px'; QS('deskarea3').height = (desktop.m.ScreenHeight * fullscreenzoom) + 'px'; QS('deskarea3')['padding-right'] = '55px'; } if (currentDevicePanel == 5) { QS('p10terminal').top = '0px'; QS('p10terminal').overflow = 'scroll'; QS('termarea3').top = '0px'; QS('termarea3').bottom = null; QS('termarea3').right = null; QS('termarea3')['padding-right'] = '55px'; QS('termarea3')['height'] = '100%'; } QS('body')['background-color'] = '#000'; QS('p10')['background-color'] = '#000'; } else { QS('DeskParent').height = '100%'; QS('page_content').top = '50px'; QS('page_content').bottom = '32px'; if (currentDevicePanel == 1) { QS('p10desktop').top = '55px'; QS('p10desktop').overflow = 'hidden'; QS('deskarea3').top = '32px'; QS('deskarea3').left = null; QS('deskarea3').width = '100%'; QS('deskarea3').height = 'calc(100% - 64px)'; QS('deskarea3')['padding-right'] = ''; QS('DeskParent')['margin-top'] = null; QS('DeskParent')['margin-left'] = null; } if (currentDevicePanel == 5) { //xterm.setOption('fontSize', 15) QS('p10terminal').top = '55px'; QS('p10terminal').overflow = 'hidden'; Q('p10terminal').scrollTop = 0; Q('p10terminal').scrollLeft = 0; QS('termarea3').top = '32px'; QS('termarea3').bottom = '32px'; //QS('termarea3').right = '0px'; QS('termarea3')['padding-right'] = null; QS('termarea3')['height'] = 'calc(100% - 60px)'; } QS('body')['background-color'] = nightMode ? '#000' : '#FFF'; QS('p10')['background-color'] = null; } if (currentDevicePanel == 1) { deskAdjust(); } } function deskAdjust() { if (currentDevicePanel != 1) return; // If not on desktop tab, ignore this. if (fullscreen) { QS('Desk')['margin-top'] = null; QS('Desk')['margin-bottom'] = null; QS('Desk').width = '100%'; QS('Desk').height = '100%'; var parentH = Q('p10desktop').clientHeight, parentW = Q('p10desktop').clientWidth; var deskH = Q('deskarea3').clientHeight, deskW = Q('deskarea3').clientWidth - 55; if (parentH > deskH) { QS('deskarea3').top = ((parentH - deskH) / 2) + 'px'; } else { QS('deskarea3').top = null; } if (parentW > deskW) { QS('deskarea3').left = ((parentW - deskW) / 2) + 'px'; } else { QS('deskarea3').left = null; } } else { var parentH = Q('DeskParent').clientHeight, parentW = Q('DeskParent').clientWidth; var deskH = Q('Desk').height, deskW = Q('Desk').width; var webPageFullScreen = false; // Fixed aspect ratio if ((parentH / parentW) > (deskH / deskW)) { var hNew = ((deskH * parentW) / deskW) + 'px'; QS('Desk').height = hNew; QS('Desk').width = '100%'; } else { var wNew = ((deskW * parentH) / deskH) + 'px'; QS('Desk').width = wNew; QS('Desk').height = '100%'; } QS('DeskParent').overflow = 'hidden'; // Adjust top/bottom margins var x = (Q('DeskParent').clientHeight - Q('Desk').clientHeight) / 2; QS('Desk')['margin-top'] = x + 'px'; QS('Desk')['margin-bottom'] = x + 'px'; } } function sendSpecialKeys() { if (xxdialogMode || desktop == null || desktop.State != 3) return; setDialogMode(3, "Special Keys", 3, deskSendKeys); } // Save the desktop image to file function deskSaveImage() { setSessionActivity(); if (xxdialogMode || desktop == null || desktop.State != 3) return; var d = new Date(), n = 'Desktop-' + currentNode.name + '-' + d.getFullYear() + '-' + ('0' + (d.getMonth() + 1)).slice(-2) + '-' + ('0' + d.getDate()).slice(-2) + '-' + ('0' + d.getHours()).slice(-2) + '-' + ('0' + d.getMinutes()).slice(-2); Q('Desk')['toBlob'](function (blob) { saveAs(blob, n + '.png'); }); } function deskSelectScreens() { if (xxdialogMode || desktop == null || desktop.State != 3) return; var x = '', info = desktop.m.displays; for (var i in info) { x += '<option value=' + i + ' ' + ((desktop.m.selectedDisplay == i) ? ' selected' : '') + '>' + info[i] + '</option>'; } x = addHtmlValue4("Screen", '<select style=width:100% id=deskdisplays>' + x + '</select>'); setDialogMode(2, "Screen Selection", 3, deskSelectScreensEx, x); } function deskSelectScreensEx() { if (desktop == null || desktop.State != 3) return; desktop.m.SetDisplay(parseInt(Q('deskdisplays').value)); } function deskDisplayInfo(sender, info, selDisplay, selItem) { var displayCount = 0; for (var x in info) { displayCount++; } QV('DeskScreens', displayCount > 1); } function dmousedown(e) { setSessionActivity(); if ((!xxdialogMode && desktop != null)) { if (fullscreen) { e.addx = Q('p10desktop').scrollLeft * (1 / fullscreenzoom); e.addy = Q('p10desktop').scrollTop * (1 / fullscreenzoom); } desktop.m.mousedown(e); } } function dmouseup(e) { setSessionActivity(); if ((!xxdialogMode && desktop != null)) { if (fullscreen) { e.addx = Q('p10desktop').scrollLeft * (1 / fullscreenzoom); e.addy = Q('p10desktop').scrollTop * (1 / fullscreenzoom); } desktop.m.mouseup(e); } } function dmousemove(e) { setSessionActivity(); if ((!xxdialogMode && desktop != null)) { if (fullscreen) { e.addx = Q('p10desktop').scrollLeft * (1 / fullscreenzoom); e.addy = Q('p10desktop').scrollTop * (1 / fullscreenzoom); } desktop.m.mousemove(e); } } function dmousewheel(e) { setSessionActivity(); if ((!xxdialogMode && desktop != null) && desktop.m.mousewheel) { if (fullscreen) { e.addx = Q('p10desktop').scrollLeft * (1 / fullscreenzoom); e.addy = Q('p10desktop').scrollTop * (1 / fullscreenzoom); } desktop.m.mousewheel(e); haltEvent(e); return true; } return false; } function drotate(x) { if (!xxdialogMode && desktop != null) { desktop.m.setRotation(desktop.m.rotation + x); deskAdjust(); } } // // TERMINAL // var terminalNode; function setupTerminal() { // Setup the terminal if ((terminalNode != currentNode) && (terminal != null)) { terminal.Stop(); terminal = null; } terminalNode = currentNode; updateTerminalButtons(); } // Show and enable the right buttons function updateTerminalButtons() { var mtype = (currentNode.agent == 1) ? 1 : 2; var termState = ((terminal != null) && (terminal.state != 0)); QE('termFullScreen', (termState != 0)); // If we are looking at a local non-windows device, enable terminal and files capability. if ((terminalNode.mtype == 3) && (terminalNode.agent != null) && (terminalNode.agent.id > 4) && (features2 & 0x00000200)) { terminalNode.agent.caps = 6; } // Show the right buttons QV('disconnectbutton2span', (termState == true)); QV('connectbutton2span', (termState == false) && (currentNode.agent != null) && (currentNode.agent.caps & 2)); // Enable buttons var online = ((terminalNode.conn & 1) != 0) || (terminalNode.mtype == 3); // If Agent (1) connected, enable Terminal QE('connectbutton2', online); // Enable action button if mesh type is not "local devices" QV('termActionsBtn', terminalNode.mtype != 3); if (terminalNode.mtype != 3) { QH('terminalCustomUpperRight', ''); } else { QH('terminalCustomUpperRight', '<a style=cursor:pointer onclick=cmsshportaction(1,event)>' + format("SSH Port {0}", (terminalNode.sshport ? terminalNode.sshport : 22)) + '</a>'); } } function cmsshportaction(action) { if (xxdialogMode) return; var x = "SSH remote connection port:" + '<br /><br /><input type=text placeholder="22" inputmode="numeric" pattern="[0-9]*" onkeypress="return (event.keyCode == 8) || (event.charCode >= 48 && event.charCode <= 57)" maxlength=5 id=d10sshport type=text>'; setDialogMode(2, "SSH Connection", 3, function () { // Save the new SSH port to the server var sshport = ((Q('d10sshport').value.length > 0) ? parseInt(Q('d10sshport').value) : 22); meshserver.send({ action: 'changedevice', nodeid: currentNode._id, sshport: sshport }); }, x, currentNode); Q('d10sshport').focus(); if (currentNode.sshport != null) { Q('d10sshport').value = currentNode.sshport; } } // Called when the terminal state changes function onTerminalStateChange(xterminal, state) { var xstate = state; if ((xstate == 3) && (xterminal.contype == 2)) { xstate++; } var str = StatusStrs[xstate]; if (terminal.webRtcActive == true) { str += ", WebRTC"; } QH('termstatus', str); switch (state) { case 0: // Disconnected, clear the terminal xterm.dispose(); xterm = null; if (terminal != null) { terminal.Stop(); terminal = null; } break; case 3: xterm.focus(); break; default: //console.log('Unhandled onTerminalStateChange state', state); break; } updateTerminalButtons(); } // Handles a tunnel to a remote shell function CreateRemoteTunnel(onTunnelUpdate, options) { var obj = { protocol: 1 }; if ((options != null) && (typeof options.protocol == 'number')) { obj.protocol = options.protocol; } obj.onTunnelUpdate = onTunnelUpdate; obj.xxStateChange = function (state) { } obj.ProcessBinaryData = function (data) { obj.onTunnelUpdate(data); } obj.ProcessData = function (data) { obj.onTunnelUpdate(data); } obj.terminalEmulation = 1; obj.fxEmulation = 0; obj.lineFeed = '\r\n'; return obj; } function tunnelUpdate(data) { if (typeof data == 'string') { xterm.writeUtf8(data); } else { xterm.writeUtf8(new Uint8Array(data)); } } function sshTunnelUpdate(data) { if (typeof data == 'string') { if (data[0] == '{') { var j = JSON.parse(data); switch (j.action) { case 'sshauth': { var x = ''; x += addHtmlValue("Authentication", '<select id=dp2authmethod style=width:150px onchange=sshAuthUpdate(event)><option value=1 selected>' + "Username & Password" + '</option><option value=2>' + "Username and Key" + '</option></select>') x += addHtmlValue("Username", '<input id=dp2user style=width:150px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />'); x += '<div id=d2passauth>'; x += addHtmlValue("Password", '<input type=password id=dp2pass style=width:150px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />'); x += '</div><div id=d2keyauth style=display:none>'; x += addHtmlValue("Key File", '<input type=file id=dp2key style=width:150px maxlength=64 autocomplete=off onchange=sshAuthUpdate(event) />'); x += addHtmlValue("Key Password", '<input type=password id=dp2keypass style=width:150px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />'); x += '</div>'; x += '<label><input id=dp2keep type=checkbox>' + "Remember credentials" + '</label>'; x += '<div id=d2keyauth2 style=font-size:x-small><br />' + "Key file must be in OpenSSH format." + '</div>'; setDialogMode(2, "Authentication", 11, sshConnectEx, x, 'ssh'); setTimeout(sshAuthUpdate, 50); break; } case 'sshautoauth': { terminal.socket.send(JSON.stringify({ action: 'sshautoauth', cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight })); break; } case 'autherror': { p12setConsoleMsg("Authentication Error", 5000); break; } case 'sessionerror': { p12setConsoleMsg("Session expired", 5000); break; } case 'sessiontimeout': { p12setConsoleMsg("Session timeout", 5000); break; } } } else if (data[0] == '~') { xterm.writeUtf8(data.substring(1)); } } } function sshAuthUpdate(e) { QV('d2passauth', Q('dp2authmethod').value == 1); QV('d2keyauth', Q('dp2authmethod').value == 2); QV('d2keyauth2', Q('dp2authmethod').value == 2); if (Q('dp2authmethod').value == 1) { QE('idx_dlgOkButton', (Q('dp2user').value.length > 0) && (Q('dp2pass').value.length > 0)); } else { QE('idx_dlgOkButton', false); var ok = (Q('dp2user').value.length > 0) && (Q('dp2key').files != null) && (Q('dp2key').files.length == 1) && (Q('dp2key').files[0].size < 8000); if (ok == true) { var reader = new FileReader(); reader.onload = function (e) { var validkey = ((e.target.result.indexOf('-----BEGIN OPENSSH PRIVATE KEY-----') >= 0) && (e.target.result.indexOf('-----END OPENSSH PRIVATE KEY-----') >= 0)); QE('idx_dlgOkButton', validkey); } reader.readAsText(Q('dp2key').files[0]); } } } function sshConnectEx(b) { if (b == 0) { if (terminal != null) { connectTerminal(); } // Disconnect } else { if (Q('dp2authmethod').value == 1) { terminal.socket.send(JSON.stringify({ action: 'sshauth', username: Q('dp2user').value, password: Q('dp2pass').value, keep: Q('dp2keep').checked, cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight })); } else { var reader = new FileReader(), username = Q('dp2user').value, keypass = Q('dp2keypass').value, keep = Q('dp2keep').checked; reader.onload = function (e) { terminal.socket.send(JSON.stringify({ action: 'sshauth', username: username, keypass: keypass, key: e.target.result, keep: keep, cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight })); } reader.readAsText(Q('dp2key').files[0]); } } } // Send the new terminal size to the agent function xTermSendResize() { xtermResizeTimer = null; if ((xterm != null) && (terminal != null) && (terminal.sendCtrlMsg != null)) { if (terminal.urlname == 'sshterminalrelay.ashx') { terminal.socket.send(JSON.stringify({ action: 'resize', cols: xterm.cols, rows: xterm.rows, width: Q('termarea3xdiv').offsetWidth, height: Q('termarea3xdiv').offsetHeight })); } else { terminal.sendCtrlMsg(JSON.stringify({ ctrlChannel: '102938', type: 'termsize', cols: xterm.cols, rows: xterm.rows })); } } } function connectTerminal(e, contype, options) { p12clearConsoleMsg(); if (!terminal) { // Terminal setup var termoptions = { protocol: ((options != null) && (typeof options.protocol == 'number')) ? options.protocol : 1 }; if (options && options.requireLogin) { termoptions.requireLogin = true; } /* if ([1, 2, 3, 4, 21, 22].indexOf(currentNode.agent.id) == -1) { if (Q('termSizeList').value == 1) { termoptions.cols = 80; termoptions.rows = 25; termoptions.xterm = true; } else if (Q('termSizeList').value == 2) { termoptions.cols = 100; termoptions.rows = 30; termoptions.xterm = true; } else if (Q('termSizeList').value == 3) { // TODO: Try to improve terminal auto-size. termoptions.cols = Math.floor((Q('column_l').clientWidth - 60) / 10); termoptions.rows = Math.floor((Q('column_l').clientHeight - 120) / 20); termoptions.xterm = true; } } // If shift is pressed if ((e && (e.shiftKey == true))) { if (currentNode.agent.id > 4) { if (termoptions.protocol == 1) { termoptions.protocol = 7; } // Switch to user shell } else { if (termoptions.protocol == 1) { termoptions.protocol = 6; } // Switch to Powershell } } // If the server requires a shell type if ((serverinfo.linuxshell) != null && (currentNode.agent.id > 4)) { if (serverinfo.linuxshell == 'root') { termoptions.protocol = 1; delete termoptions.requireLogin; } if (serverinfo.linuxshell == 'user') { termoptions.protocol = 8; delete termoptions.requireLogin; } if (serverinfo.linuxshell == 'login') { termoptions.protocol = 1; termoptions.requireLogin = true; } } */ // Setup a mesh agent xterm terminal QV('termarea3xdiv', true); // Setup the terminal with auto-fit if (xterm != null) { xterm.dispose(); } xterm = new Terminal(); xtermfit = new FitAddon.FitAddon(); if (xtermfit) { xterm.loadAddon(xtermfit); } xterm.setOption('scrollback', 0); //xterm.setOption('fontSize', 15); xterm.open(Q('termarea3xdiv')); xterm.onData(function (data) { if (terminal.urlname == 'sshterminalrelay.ashx') { terminal.socket.send('~' + data); } else { terminal.sendText(data); } }) if (xtermfit) { xtermfit.fit(); } xterm.onResize(function (size) { // Despam resize if (xtermResizeTimer) clearTimeout(xtermResizeTimer); xtermResizeTimer = setTimeout(xTermSendResize, 200); }); // Remove terminal textarea and scrollbar. document.getElementsByClassName('xterm-helper-textarea')[0].onfocus = () => { xterm.blur(); if (!fullscreen) toggleKeyboard(); }; document.getElementsByClassName('xterm-viewport')[0].style.overflow = 'hidden'; // Setup a terminal tunnel to the agent terminal = CreateAgentRedirect(meshserver, CreateRemoteTunnel((currentNode.mtype == 3) ? sshTunnelUpdate : tunnelUpdate, termoptions), serverPublicNamePort, authCookie, authRelayCookie, domainUrl); if (currentNode.mtype == 3) { terminal.urlname = 'sshterminalrelay.ashx'; } // If this is a SSH session, change the URL to the SSH application relay. terminal.debugmode = debugmode; terminal.m.debugmode = debugmode; terminal.options = termoptions; terminal.options = { cols: xterm.cols, rows: xterm.rows }; if (termoptions.requireLogin) { terminal.options.requireLogin = true; } terminal.Start(terminalNode._id); terminal.onStateChanged = onTerminalStateChange; terminal.contype = 1; terminal.attemptWebRTC = false; // Never do WebRTC on terminal, because of a race condition we can't do it. terminal.onConsoleMessageChange = function () { p12setConsoleMsg(terminal.consoleMessage ? formatAgentConsoleMessage(terminal.consoleMessage, terminal.consoleMessageId, terminal.consoleMessageArgs) : null, terminal.consoleMessageTimeout); } } else { terminal.Stop(); terminal = null; if (fullscreen) { deskToggleFull(); } } Q('connectbutton2').blur(); // Deselect the connect button so the button does not get key presses. } // // Terminal Shortcut Keys // function updateTermShortcutKeys() { var x = ''; for (var i = 64; i <= 95; i++) { x += '<div class="menuButton" style="width:70px" onclick="termMenuButton(' + i + ')">' + "Ctrl + " + String.fromCharCode(i) + '</div>'; } QH('termButtonMenu', x); } function termMenuButton(c) { toggleMenu(true); if (terminal.urlname == 'sshterminalrelay.ashx') { // SSH terminal.socket.send('~' + String.fromCharCode(c - 64)); } else { // Agent terminal.sendText(String.fromCharCode(c - 64)); } } // // FILES // var filesNode; function setupFiles() { // Setup the files tab var samenode = (filesNode == currentNode); filesNode = currentNode; var online = ((filesNode.conn & 1) != 0) || (filesNode.mtype == 3); // If Agent (1) connected, enable Terminal QE('p13Connect', online); if (((samenode == false) || (online == false)) && files) { files.Stop(); files = null; } p13setActions(); } function onFilesStateChange(xfiles, state) { setSessionActivity(); p13Connect.value = (state == 0) ? "Connect" : "Disconnect"; var str = StatusStrs[state]; if (files.webRtcActive == true) { str += ", WebRTC"; } Q('p13Status').textContent = str; switch (state) { case 0: // Disconnected, clear the files QH('p13files', ''); p13filetree = null; p13filetreelocation = []; QH('p13currentpath', ''); QE('p13FolderUp', false); p13setActions(); if (files != null) { files.Stop(); files = null; } if (uploadFile != null) { p13uploadFileTransferDone(); uploadFile = null; } break; case 3: p13targetpath = ''; files.sendText({ action: 'ls', reqid: 1, path: '' }); break; default: //console.log('Unknown onFilesStateChange state', state); break; } } function CreateRemoteFiles(onFileUpdate) { var obj = { protocol: 5 }; obj.onFileUpdate = onFileUpdate; obj.xxStateChange = function (state) { } obj.ProcessData = function (data) { obj.onFileUpdate(data); } return obj; } // Debug Only var autoConnectFilesTimer = null; function autoConnectFiles(e) { if (autoConnectFilesTimer == null) { autoConnectFilesTimer = setInterval(connectFiles, 100); } else { clearInterval(autoConnectFilesTimer); autoConnectFilesTimer = null; } } function connectFiles(e) { p13clearConsoleMsg(); if (!files) { // Setup a mesh agent files files = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotFiles), serverPublicNamePort, authCookie, authRelayCookie, domainUrl); if (filesNode.mtype == 3) { files.urlname = 'sshfilesrelay.ashx'; } // If this is a SSH session, change the URL to the SSH application relay. files.attemptWebRTC = attemptWebRTC; files.onStateChanged = onFilesStateChange; files.onConsoleMessageChange = function () { if (files.consoleMessage) { Q('p13FilesConsoleMsg').innerHTML += formatAgentConsoleMessage(files.consoleMessage, files.consoleMessageId, files.consoleMessageArgs); QV('p13FilesConsoleMsg', true); if (p13FilesConsoleMsgTimer != null) { clearTimeout(p13FilesConsoleMsgTimer); } if (files.consoleMessageTimeout) { p13FilesConsoleMsgTimer = setTimeout(p13clearConsoleMsg, files.consoleMessageTimeout * 1000); } } else { p13clearConsoleMsg(); } } files.Start(filesNode._id); } else { //QH('Term', ''); files.Stop(); files = null; } p13clipboard = p13clipboardFolder = null; p13clipboardCut = 0; p13updateClipview(); } var p13filetree = null; var p13targetpath = null; var p13filetreelocation = []; function p13gotFiles(data) { if ((data.length > 0) && (data.charCodeAt(0) != 123)) { p13gotDownloadBinaryData(data); return; } // This is ok because 4 first bytes is a control value. //console.log('p13gotFiles', data); try { data = JSON.parse(decode_utf8(data)); } catch (ex) { data = JSON.parse(data); } if (data.action == 'download') { p13gotDownloadCommand(data); return; } // Process any SSH actions switch (data.action) { case 'sshauth': { var x = ''; x += addHtmlValue("Authentication", '<select id=dp2authmethod style=width:150px onchange=sshAuthUpdate(event)><option value=1 selected>' + "Username & Password" + '</option><option value=2>' + "Username and Key" + '</option></select>') x += addHtmlValue("Username", '<input id=dp2user style=width:150px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />'); x += '<div id=d2passauth>'; x += addHtmlValue("Password", '<input type=password id=dp2pass style=width:150px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />'); x += '</div><div id=d2keyauth style=display:none>'; x += addHtmlValue("Key File", '<input type=file id=dp2key style=width:150px maxlength=64 autocomplete=off onchange=sshAuthUpdate(event) />'); x += addHtmlValue("Key Password", '<input type=password id=dp2keypass style=width:150px maxlength=64 autocomplete=off onkeyup=sshAuthUpdate(event) />'); x += '</div>'; x += '<label><input id=dp2keep type=checkbox>' + "Remember credentials" + '</label>'; x += '<div id=d2keyauth2 style=font-size:x-small><br />' + "Key file must be in OpenSSH format." + '</div>'; setDialogMode(2, "Authentication", 11, p13sshConnectEx, x, 'ssh'); setTimeout(sshAuthUpdate, 50); break; } case 'autherror': { p13setConsoleMsg("Authentication Error", 5000); return; } case 'connectionerror': { p13setConsoleMsg("Connection Error", 5000); return; } case 'sessionerror': { p13setConsoleMsg("Session expired", 5000); return; } case 'sessiontimeout': { p13setConsoleMsg("Session timeout", 5000); return; } } // Process file upload commands if ((data.action != null) && (data.action.startsWith('upload'))) { p13gotUploadData(data); return; } if (data.path != null) { data.path = data.path.replace(/\//g, '\\'); if ((p13filetree != null) && (data.path == p13filetree.path)) { // This is an update to the same folder var checkedNames = p13getCheckedNames(); p13filetree = data; p13updateFiles(checkedNames); } else { // Make both paths use the same seperator not start with / var x1 = data.path.replace(/\//g, '\\'), x2 = p13targetpath.replace(/\//g, '\\'); while ((x1.length > 0) && (x1[0] == '\\')) { x1 = x1.substring(1); } while ((x2.length > 0) && (x2[0] == '\\')) { x2 = x2.substring(1); } if ((x1 == x2) || ((data.path == '\\') && (p13targetpath == ''))) { // This is a different folder p13filetree = data; p13updateFiles(); } } } } function p13sshConnectEx(b) { if (b == 0) { if (files != null) { connectFiles(); } // Disconnect } else { if (Q('dp2authmethod').value == 1) { files.socket.send(JSON.stringify({ action: 'sshauth', username: Q('dp2user').value, password: Q('dp2pass').value, keep: Q('dp2keep').checked })); } else { var reader = new FileReader(), username = Q('dp2user').value, keypass = Q('dp2keypass').value, keep = Q('dp2keep').checked; reader.onload = function (e) { files.socket.send(JSON.stringify({ action: 'sshauth', username: username, keypass: keypass, key: e.target.result, keep: keep })); } reader.readAsText(Q('dp2key').files[0]); } } } function p13getCheckedNames() { // Save all existing checked boxes var checkedNames = [], checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { checkedNames.push(p13filetree.dir[checkboxes[i].value].n) }; } return checkedNames; } function p13updateFiles(checkedNames) { var html1 = '', html2 = '', displayPath = '<a style=cursor:pointer;color:black onclick=p13folderup(0)>' + "Root" + '</a>', fullPath = 'Root'; // Work on parsing the file path var x = p13filetree.path.split('\\'); p13filetreelocation = []; for (var i in x) { if (x[i] != '') { p13filetreelocation.push(x[i]); } } // Remove empty spaces for (var i in p13filetreelocation) { displayPath += ' / <a style=cursor:pointer;color:black onclick=p13folderup(' + (parseInt(i) + 1) + ')>' + EscapeHtml(p13filetreelocation[i]) + '</a>' } // Setup the path we display var newlinkpath = p13filetreelocation.join('/'); // Sort the files var filetreexx = p13sort_files(p13filetree.dir); // Display all files and folders at this location for (var i in filetreexx) { // Figure out the name and shortname var f = filetreexx[i], name = f.n, shortname; shortname = name; if (name.length > 70) { shortname = EscapeHtml(name.substring(0, 70)) + "..."; } else { shortname = EscapeHtml(name); } name = EscapeHtml(name); // Figure out the size var fsize = ''; if (f.s != null) { fsize = getFileSizeStr(f.s); } var h = ''; if (f.t < 3) { var right = ''; h = '<div class=filelist file=999><input file=999 style=float:left name=fd class=fcb type=checkbox onchange=p13setActions() value=\'' + f.nx + '\'> <span style=float:right>' + right + '</span><span><div class=fileIcon' + f.t + '></div><a style=cursor:pointer onclick=p13folderset("' + encodeURIComponent(f.nx) + '")>' + shortname + '</a></span></div>'; } else { var link = shortname; if (f.s > 0) { link = '<a rel=\"noreferrer noopener\" target=\"_blank\" style=cursor:pointer onclick=\"p13downloadfile(\'' + encodeURIComponent(newlinkpath + '/' + name) + '\',\'' + encodeURIComponent(name) + '\',' + f.s + ')\">' + shortname + '</a>'; } h = '<div class=filelist file=3><input file=3 style=float:left name=fd class=fcb type=checkbox onchange=p13setActions() value=\'' + f.nx + '\'> <span style=float:right;padding-right:4px>' + fsize + '</span><span><div class=fileIcon' + f.t + '></div>' + link + '</span></div>'; } if (f.t < 3) { html1 += h; } else { html2 += h; } } // Display the files and path QH('p13files', html1 + html2); QH('p13currentpath', displayPath); QE('p13FolderUp', p13filetreelocation.length != 0); // Re-check all boxes if needed using names if (checkedNames != null) { var checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if (checkedNames.indexOf(p13filetree.dir[checkboxes[i].value].n) >= 0) { checkboxes[i].checked = true; } } } // Update the actions buttons p13setActions(); } function p13folderset(x) { p13targetpath = joinPaths(p13filetree.path, p13filetree.dir[x].n).split('\\').join('/'); files.sendText({ action: 'ls', reqid: 1, path: p13targetpath }); } function p13folderup(x) { if (x == null) { p13filetreelocation.pop(); } else { while (p13filetreelocation.length > x) { p13filetreelocation.pop(); } } p13targetpath = p13filetreelocation.join('/'); files.sendText({ action: 'ls', reqid: 1, path: p13targetpath }); } var p13sortorder; function p13sort_filename(a, b) { if (a.ln > b.ln) return (1 * p13sortorder); if (a.ln < b.ln) return (-1 * p13sortorder); return 0; } function p13sort_timestamp(a, b) { if (a.d > b.d) return (1 * p13sortorder); if (a.d < b.d) return (-1 * p13sortorder); return 0; } function p13sort_bysize(a, b) { if (a.s == b.s) return p13sort_filename(a, b); return (((a.s - b.s)) * p13sortorder); } function p13sort_files(files) { var r = [], sortselection = Q('p13sortdropdown').value; for (var i in files) { files[i].nx = i; if (files[i].s == null) { files[i].s = 0; } if (files[i].n == null) { files[i].n = i; } files[i].ln = files[i].n.toLowerCase(); r.push(files[i]); } p13sortorder = 1; if (sortselection > 3) { p13sortorder = -1; sortselection -= 3; } if (sortselection == 1) { r.sort(p13sort_filename); } else if (sortselection == 2) { r.sort(p13sort_bysize); } else if (sortselection == 3) { r.sort(p13sort_timestamp); } return r; } function p13setActions() { var advancedFeatures = (currentNode.agent.id != 14); // Reduct file feature on some devices. if (p13filetree == null) { QE('p13DeleteFileButton', false); QE('p13NewFolderButton', false); QE('p13UploadButton', false); QE('p13RenameFileButton', false); QE('p13SelectAllButton', false); Q('p13SelectAllButton').value = "All"; QE('p13RefreshButton', false); QE('p13CutButton', false); QE('p13CopyButton', false); QE('p13PasteButton', false); } else { var cc = p13getFileSelCount(), tc = p13getFileCount(), sfc = p13getFileSelCount(false); // In order: number of entires selected, number of total entries, number of selected entires that are files (not folders) var winAgent = ((currentNode.agent.id > 0) && (currentNode.agent.id < 5)) || (currentNode.agent.id == 14) || (currentNode.agent.id == 34); QE('p13DeleteFileButton', advancedFeatures && (cc > 0) && ((p13filetreelocation.length > 0) || (winAgent == false))); QE('p13NewFolderButton', advancedFeatures && ((p13filetreelocation.length > 0) || (winAgent == false))); QE('p13UploadButton', advancedFeatures && ((p13filetreelocation.length > 0) || (winAgent == false))); QE('p13RenameFileButton', advancedFeatures && (cc == 1) && ((p13filetreelocation.length > 0) || (winAgent == false))); QE('p13SelectAllButton', tc > 0); Q('p13SelectAllButton').value = (cc > 0 ? "None" : "All"); QE('p13RefreshButton', true); QE('p13CutButton', advancedFeatures && (cc > 0) && (cc == sfc) && (currentNode.mtype != 3) && ((p13filetreelocation.length > 0) || (winAgent == false))); QE('p13CopyButton', advancedFeatures && (cc > 0) && (cc == sfc) && (currentNode.mtype != 3) && ((p13filetreelocation.length > 0) || (winAgent == false))); QE('p13PasteButton', advancedFeatures && (currentNode.mtype != 3) && ((p13filetreelocation.length > 0) || (winAgent == false)) && ((p13clipboard != null) && (p13clipboard.length > 0))); } if (filesNode.mtype != 3) { QH('filesCustomUpperRight', ''); } else { QH('filesCustomUpperRight', '<a style=cursor:pointer onclick=cmsshportaction(1,event)>' + format("SSH Port {0}", (filesNode.sshport ? filesNode.sshport : 22)) + '</a>'); } QV('filesActionsBtn', filesNode.mtype != 3); } function p13getFileSelCount(includeDirs) { var cc = 0; var checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && ((includeDirs != false) || (checkboxes[i].attributes.file.value == '3'))) cc++; } return cc; } function p13getFileSelDirCount() { var cc = 0, checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '999')) cc++; } return cc; } function p13getFileCount() { var cc = 0; var checkboxes = document.getElementsByName('fd'); return checkboxes.length; } function p13selectallfile() { var nv = (p13getFileSelCount() == 0), checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { checkboxes[i].checked = nv; } p13setActions(); } function p13createfolder() { setDialogMode(2, "New Folder", 3, p13createfolderEx, '<input type=text id=p13renameinput maxlength=64 onkeyup=p13fileNameCheck(event) style=width:100% />'); focusTextBox('p13renameinput'); p13fileNameCheck(); } function p13createfolderEx() { files.sendText({ action: 'mkdir', reqid: 1, path: p13filetreelocation.join('/') + '/' + Q('p13renameinput').value }); p13folderup(999); } function p13deletefile() { var cc = p13getFileSelCount(), rec = (p13getFileSelDirCount() > 0) ? '<br /><br /><label><input type=checkbox id=p13recdeleteinput>' + "Recursive delete" + '</label><br>' : '<input type=checkbox id=p13recdeleteinput style=\'display:none\'>'; setDialogMode(2, "Delete", 3, p13deletefileEx, (cc > 1) ? (format("Delete {0} selected items?", cc) + rec) : ("Delete selected item?" + rec)); } function p13deletefileEx() { var delfiles = [], checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { delfiles.push(p13filetree.dir[checkboxes[i].value].n); } } files.sendText({ action: 'rm', reqid: 1, path: p13filetreelocation.join('/'), delfiles: delfiles, rec: Q('p13recdeleteinput').checked }); p13folderup(999); } function p13renamefile() { var renamefile, checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { renamefile = p13filetree.dir[checkboxes[i].value].n; } } setDialogMode(2, "Rename", 3, p13renamefileEx, '<input type=text id=p13renameinput maxlength=64 onkeyup=p13fileNameCheck(event) style=width:100% value="' + renamefile + '" />', { action: 'rename', path: p13filetreelocation.join('/'), oldname: renamefile }); focusTextBox('p13renameinput'); p13fileNameCheck(); } function p13renamefileEx(b, t) { t.newname = Q('p13renameinput').value; files.sendText(t); p13folderup(999); } function p13fileNameCheck(e) { var x = isFilenameValid(Q('p13renameinput').value); QE('idx_dlgOkButton', x); if ((x == true) && (e != null) && (e.keyCode == 13)) { dialogclose(1); } } function p13uploadFile() { setDialogMode(2, "Upload File", 3, p13uploadFileEx, '<input type=file name=files id=p13uploadinput style=width:100% multiple=multiple onchange="updateUploadDialogOk(\'p13uploadinput\')" />'); updateUploadDialogOk('p13uploadinput'); } function p13uploadFileEx() { p13doUploadFiles(Q('p13uploadinput').files); } function p13viewfile() { var checkboxes = document.getElementsByName('fd'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { if (p13filetree.dir[checkboxes[i].value].s <= 204800) { p13downloadfile(encodeURIComponent(p13filetreelocation.join('/') + '/' + p13filetree.dir[checkboxes[i].value].n), encodeURIComponent(p13filetree.dir[checkboxes[i].value].n), p13filetree.dir[checkboxes[i].value].s, 'viewer'); } else { messagebox("File Editor", "Only files less than 200k can be edited."); } break; } } } var p13clipboard = null, p13clipboardFolder = null, p13clipboardCut = 0; function p13copyFile(cut) { var checkboxes = document.getElementsByName('fd'); p13clipboard = []; p13clipboardCut = cut, p13clipboardFolder = p13targetpath; for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && (checkboxes[i].attributes.file.value == '3')) { p13clipboard.push(p13filetree.dir[checkboxes[i].value].n); } } p13updateClipview(); } function p13pasteFile() { var x = ''; if ((p13clipboard != null) && (p13clipboard.length > 0)) { if (p13clipboardCut == 0) { if (p13clipboard.length > 1) { x = format("Confirm copy of {0} entries's to this location?", p13clipboard.length); } else { x = format("Confirm copy of 1 entrie to this location?"); } } else { if (p13clipboard.length > 1) { x = format("Confirm move of {0} entries's to this location?", p13clipboard.length); } else { x = format("Confirm move of 1 entrie to this location?"); } } } setDialogMode(2, "Paste", 3, p13pasteFileEx, x); } function p13pasteFileEx() { files.sendText({ action: (p13clipboardCut == 0 ? 'copy' : 'move'), reqid: 1, scpath: p13clipboardFolder, dspath: p13targetpath, names: p13clipboard }); p13folderup(999); if (p13clipboardCut == 1) { p13clipboard = null, p13clipboardFolder = null, p13clipboardCut = 0; p13updateClipview(); } } function p13updateClipview() { var x = ''; if ((p13clipboard != null) && (p13clipboard.length > 0)) { if (p13clipboardCut == 0) { if (p13clipboard.length > 1) { x = format("Holding {0} entries for copy" + ', <a href=# onclick="return p13clearClip()" style=cursor:pointer>' + "Clear" + '</a>.', p13clipboard.length); } else { x = format("Holding 1 entrie for copy" + ', <a href=# onclick="return p13clearClip()" style=cursor:pointer>' + "Clear" + '</a>.'); } } else { if (p13clipboard.length > 1) { x = format("Holding {0} entries for move" + ', <a href=# onclick="return p13clearClip()" style=cursor:pointer>' + "Clear" + '</a>.', p13clipboard.length); } else { x = format("Holding 1 entrie for move" + ', <a href=# onclick="return p13clearClip()" style=cursor:pointer>' + "Clear" + '</a>.'); } } } QH('p13bottomstatus', x); p13setActions(); } function p13clearClip() { p13clipboard = null; p13clipboardFolder = null; p13clipboardCut = 0; p13updateClipview(); return false; } function updateUploadDialogOk(x) { QE('idx_dlgOkButton', Q(x).value != ''); } function getFileSelCount(includeDirs) { var cc = 0; var checkboxes = document.getElementsByName('fc'); for (var i = 0; i < checkboxes.length; i++) { if ((checkboxes[i].checked) && ((includeDirs != false) || (checkboxes[i].attributes.file.value == "3"))) cc++; } return cc; } function getFileCount() { var cc = 0; var checkboxes = document.getElementsByName('fc'); return checkboxes.length; } // // FILES DOWNLOAD // var downloadFile; // Global state for file download // Called by the html page to start a download, arguments are: path, file name and file size. function p13downloadfile(x, y, z) { if (xxdialogMode || downloadFile || !files) return; downloadFile = { path: decodeURIComponent(x), file: decodeURIComponent(y), size: z, tsize: 0, data: '', state: 0, id: Math.random() } //console.log('p13downloadFileCancel', downloadFile); files.sendText({ action: 'download', sub: 'start', id: downloadFile.id, path: downloadFile.path }); setDialogMode(2, "Download File", 10, p13downloadFileCancel, '<div>' + downloadFile.file + '</div><br /><progress id=d2progressBar style=width:100% value=0 max=' + z + ' />'); } // Called by the html page to cancel the download function p13downloadFileCancel() { setDialogMode(0); files.sendText({ action: 'download', sub: 'cancel', id: downloadFile.id }); downloadFile = null; } // Called by the transport when download control command is received function p13gotDownloadCommand(cmd) { //console.log('p13gotDownloadCommand', cmd); if ((downloadFile == null) || (cmd.id != downloadFile.id)) return; if (cmd.sub == 'start') { downloadFile.state = 1; files.sendText({ action: 'download', sub: 'startack', id: downloadFile.id }); } else if (cmd.sub == 'cancel') { downloadFile = null; setDialogMode(0); } } // Called by the transport when binary data is received function p13gotDownloadBinaryData(data) { if (!downloadFile || downloadFile.state == 0) return; if (data.length > 4) { downloadFile.tsize += (data.length - 4); // Add to the total bytes received downloadFile.data += data.substring(4); // Append the data Q('d2progressBar').value = downloadFile.tsize; // Change the progress bar } if ((ReadInt(data, 0) & 1) != 0) { // Check end flag saveAs(data2blob(downloadFile.data), downloadFile.file); downloadFile = null; setDialogMode(0); // Save the file } else { files.sendText({ action: 'download', sub: 'ack', id: downloadFile.id }); // Send the ACK } } /* var downloadFile; // Global state for file download // Called by the html page to start a download, arguments are: path, file name and file size. function p13downloadfile(x, y, z) { if (xxdialogMode) return; downloadFile = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotDownloadData), serverPublicNamePort, authCookie, authRelayCookie, domainUrl); // Create our websocket file transport downloadFile.ctrlMsgAllowed = false; downloadFile.onStateChanged = onFileDownloadStateChange; downloadFile.xpath = decodeURIComponent(x); downloadFile.xfile = decodeURIComponent(y); downloadFile.xsize = z; downloadFile.xtsize = 0; downloadFile.xstate = 0; downloadFile.Start(filesNode._id); setDialogMode(2, "Download File", 10, p13downloadFileCancel, '<div>' + downloadFile.xfile + '</div><br /><progress id=d2progressBar style=width:100% value=0 max=' + z + ' />'); } // Called by the html page to cancel the download function p13downloadFileCancel(button, tag) { //console.log('p13downloadFileCancel'); downloadFile.Stop(); delete downloadFile; downloadFile = null; } // Called by the file transport to indicate when the transport connection state has changed function onFileDownloadStateChange(xdownloadFile, state) { switch (state) { case 0: // Transport as disconnected. If this is not part of an abort, we need to save the file setDialogMode(0); // Close any dialog boxes if present if ((downloadFile != null) && (downloadFile.xstate == 1)) { saveAs(data2blob(downloadFile.xdata), downloadFile.xfile); } // Save the file break; case 3: // Transport as connected, send a command to indicate we want to start a file download downloadFile.send(JSON.stringify({ action: 'download', reqid: 1, path: downloadFile.xpath })); break; default: console.log('Unknown onFileDownloadStateChange state', state); break; } } // Called by the transport when data is received function p13gotDownloadData(data) { if (downloadFile.xstate == 0) { // If state is 0, this is a command confirming if the file will be transfered. var cmd = JSON.parse(data); if (cmd.action == 'downloadstart') { // Yes, the file is about to start downloadFile.xstate = 1; // Switch to state 1, we will start receiving the file data downloadFile.xdata = ''; // Start with empty data downloadFile.send('a'); // Send the first ACK } else if (cmd.action == 'downloaderror') { // Problem opening this file, cancel p13downloadFileCancel(); } } else { // We are in the process of receiving the file downloadFile.xtsize += (data.length); // Add to the total bytes received downloadFile.xdata += data; // Append the data Q('d2progressBar').value = downloadFile.xtsize; // Change the progress bar downloadFile.send('a'); // Send the ACK } } */ // // FILES UPLOAD // var uploadFile; function p13doUploadFiles(files) { if (xxdialogMode) return; // Check if we are going to overwrite any files var winAgent = ((currentNode.agent.id > 0) && (currentNode.agent.id < 5)) || (currentNode.agent.id == 14) || (currentNode.agent.id == 34); var targetFiles = [], overWriteCount = 0; for (var i in p13filetree.dir) { if (winAgent) { targetFiles.push(p13filetree.dir[i].n.toLowerCase()); } else { targetFiles.push(p13filetree.dir[i].n); } } for (var i = 0; i < files.length; i++) { if (winAgent) { if (targetFiles.indexOf(files[i].name.toLowerCase()) >= 0) { overWriteCount++; } } else { if (targetFiles.indexOf(files[i].name) >= 0) { overWriteCount++; } } } if (overWriteCount == 0) { // If no overwrite, go ahead with upload p13uploadFileContinue(1, files); } else { // Otherwise, prompt for confirmation setDialogMode(2, "Upload File", 3, p13uploadFileContinue, format((overWriteCount == 1) ? "Upload will overwrite 1 file. Continue?" : "Upload will overwrite {0} files. Continue?", overWriteCount), files); } } function p13uploadFileContinue(b, files) { uploadFile = {}; uploadFile.xpath = p13filetreelocation.join('/'); uploadFile.xfiles = files; uploadFile.xfilePtr = -1; setDialogMode(2, "Upload File", 10, p13uploadFileCancel, '<div id=p13dfileName>' + "Connecting..." + '</div><br /><progress id=d2progressBar style=width:100% value=0 max=0 />'); p13uploadNextFile(); } // Push the next file function p13uploadNextFile() { uploadFile.xfilePtr++; if (uploadFile.xfiles.length > uploadFile.xfilePtr) { uploadFile.xptr = 0; var file = uploadFile.xfiles[uploadFile.xfilePtr]; QH('p13dfileName', file.name); Q('d2progressBar').max = file.size; Q('d2progressBar').value = 0; if (file.xdata == null) { // Load the data uploadFile.xreader = new FileReader(); uploadFile.xreader.onload = function () { uploadFile.xdata = uploadFile.xreader.result; files.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xdata.byteLength })); }; uploadFile.xreader.readAsArrayBuffer(file); } else { // Data already loaded uploadFile.xdata = file.xdata; files.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xdata.byteLength })); } } else { p13uploadFileTransferDone(); } } // Used to cancel the entire transfer. function p13uploadFileCancel(button, tag) { if (uploadFile != null) { files.sendText(JSON.stringify({ action: 'uploadcancel', reqid: uploadFile.xfilePtr })); uploadFile = null; } p13uploadFileTransferDone(); } // Used to cancel the entire transfer. function p13uploadFileTransferDone() { uploadFile = null; // No more files to upload, clean up. setDialogMode(0); // Close the dialog box p13folderup(9999); // Refresh the current folder } // Receive upload ack from the mesh agent, use this to keep sending more data function p13gotUploadData(cmd) { if ((uploadFile == null) || (parseInt(uploadFile.xfilePtr) != parseInt(cmd.reqid))) { return; } switch (cmd.action) { case 'uploadstart': { p13uploadNextPart(false); for (var i = 0; i < 8; i++) { p13uploadNextPart(true); } break; } // Send 8 more blocks of 16k to fill the websocket. case 'uploadack': { p13uploadNextPart(false); break; } case 'uploaddone': { if (uploadFile.xfiles.length > uploadFile.xfilePtr + 1) { p13uploadNextFile(); } else { p13uploadFileTransferDone(); } break; } case 'uploaderror': { p13uploadFileCancel(); break; } } } // Push the next part of the file into the websocket. If dataPriming is true, push more data only if it's not the last block of the file. function p13uploadNextPart(dataPriming) { var data = uploadFile.xdata, start = uploadFile.xptr; if (start >= data.byteLength) { files.sendText(JSON.stringify({ action: 'uploaddone', reqid: uploadFile.xfilePtr })); } else { var end = uploadFile.xptr + 16384; if (end > data.byteLength) { if (dataPriming == true) { return; } end = data.byteLength; } var dataslice = new Uint8Array(data.slice(start, end)) if ((dataslice[0] == 123) || (dataslice[0] == 0)) { var datapart = new Uint8Array(end - start + 1); datapart.set(dataslice, 1); // Add a zero char at the start of the send, this will indicate that it's not a JSON command. files.send(datapart); } else { files.send(dataslice); // The data does not start with 0 or 123 "{" so it can't be confused for JSON. } uploadFile.xptr = end; Q('d2progressBar').value = end; } } // // DEVICE DETAILS // var DeviceDetailsHardware = null; var DeviceDetailsNetwork = null; var DeviceDetailsNodeId = null; function updateDeviceDetails(node, hardware, network) { if (currentNode == null) return; if (node == null) { node = currentNode; } if (currentNode._id != node._id) return; if (DeviceDetailsNodeId != node._id) { DeviceDetailsHardware = null; DeviceDetailsNetwork = null; DeviceDetailsNodeId = node._id; } if (hardware != null) { DeviceDetailsHardware = hardware; } if (network != null) { DeviceDetailsNetwork = network; } hardware = DeviceDetailsHardware; network = DeviceDetailsNetwork; if (hardware == null) { hardware = {}; } if (network == null) { network = {}; } var sections = [], s = {}; // Operating System if ((hardware.windows && hardware.windows.osinfo) || node.osdesc) { var x = ''; if (node.rname) { x += addDetailItem("Name", EscapeHtml(node.rname), s); } if (node.osdesc) { x += addDetailItem("Version", EscapeHtml(node.osdesc), s); } if (hardware.windows && hardware.windows.osinfo) { var m = hardware.windows.osinfo; if (m.OSArchitecture) { if (m.OSArchitecture.startsWith('32')) { x += addDetailItem("Architecture", "32-bit", s); } else if (m.OSArchitecture.startsWith('64')) { x += addDetailItem("Architecture", "64-bit", s); } else { x += addDetailItem("Architecture", EscapeHtml(m.OSArchitecture), s); } } } // Windows Security Central if (node.wsc) { var y = []; if (node.wsc.antiVirus != null) { if (node.wsc.antiVirus == 'OK') { y.push("AV" + ' - <span style=color:green>' + "OK" + '</span>'); } else { y.push("AV" + ' - <span style=color:red>' + "BAD" + '</span>'); } } if (node.wsc.autoUpdate != null) { if (node.wsc.autoUpdate == 'OK') { y.push("Update" + ' - <span style=color:green>' + "OK" + '</span>'); } else { y.push("Update" + ' - <span style=color:red>' + "BAD" + '</span>'); } } if (node.wsc.firewall != null) { if (node.wsc.firewall == 'OK') { y.push("Firewall" + ' - <span style=color:green>' + "OK" + '</span>'); } else { y.push("Firewall" + ' - <span style=color:red>' + "BAD" + '</span>'); } } x += addDetailItem("Windows Security", y.join(', ')); } // Antivirus if (node.av && node.av.length > 0) { var y = []; for (var i in node.av) { if (node.av[i].product) { var avx = EscapeHtml(node.av[i].product); if (node.av[i].enabled !== true) { avx += ' - <span style=color:red>' + "Disabled" + '</span>'; } if (node.av[i].updated !== true) { avx += ' - <span style=color:red>' + "Out of date" + '</span>'; } if ((node.av[i].enabled == true) && (node.av[i].updated == true)) { avx += ' - <span style=color:green>' + "OK" + '</span>'; } y.push(avx); } } x += addDetailItem("Antivirus", y.join('<br />')); } if (x != '') { sections.push({ name: "Operating System", html: x, img: 'software' }); } } // MeshAgent if (node.agent) { var x = ''; if ((node.agent != null) && (node.agent.id != null) && (node.agent.ver != null)) { var str = ''; if (node.agent.id <= agentsStr.length) { str = agentsStr[node.agent.id]; } else { str = agentsStr[0]; } if (node.agent.ver != 0) { str += ' v' + node.agent.ver; } if (node.agent.id == 14) { str = node.agent.core; } x += addDetailItem("Mesh Agent", str); } if ((node.conn & 1) != 0) { x += addDetailItem("Last agent connection", "Connected now"); } else { if (node.lastconnect) { x += addDetailItem("Last agent connection", printDateTime(new Date(node.lastconnect))); } } if (node.lastaddr) { var splitip = node.lastaddr.split(':'); if (splitip.length > 2) { // IPv6 x += addDetailItem("Last agent address", node.lastaddr); } else { // IPv4 if (isPrivateIP(node.lastaddr)) { x += addDetailItem("Last agent address", splitip[0]); } else { x += addDetailItem("Last agent address", '<a href="https://iplocation.com/?ip=' + splitip[0] + '" rel="noreferrer noopener" target="MeshIPLoopup">' + splitip[0] + '</a>'); } } } if (hardware.agentvers != null) { if (hardware.agentvers.compileTime) { try { var d = Date.parse(hardware.agentvers.compileTime) x += addDetailItem("Compile time", printDateTime(new Date(d))); } catch (ex) { } } } if (x != '') { sections.push({ name: "Mesh Agent", html: x, img: 'meshagent' }); } } // Mobile if (hardware.mobile) { var x = ''; if (hardware.mobile.brand && hardware.mobile.model) { x += addDetailItem("Model", EscapeHtml(hardware.mobile.brand + ', ' + hardware.mobile.model), s); } if (hardware.mobile.device) { x += addDetailItem("Device", EscapeHtml(hardware.mobile.device), s); } if (hardware.mobile.bootloader) { x += addDetailItem("Bootloader", EscapeHtml(hardware.mobile.bootloader), s); } if (hardware.mobile.id) { x += addDetailItem("Identifier", EscapeHtml(hardware.mobile.id), s); } if (hardware.mobile.host) { x += addDetailItem("Hostname", EscapeHtml(hardware.mobile.host), s); } if (x != '') { sections.push({ name: "Mobile Device", html: x, img: 'mobile' }); } } // Networking if (network.netif2 != null) { // Display one network interface for each MAC address var x = ''; x += '<table style=width:100%>'; for (var i in network.netif2) { var m = network.netif2[i]; if ((Array.isArray(m) == false) || (m.length < 1) || (m[0] == null) || ((typeof m[0].mac == 'string') && (m[0].mac.startsWith('00:00:00:00')))) continue; x += '<tr><td><div class=style10 style=border-radius:5px;padding:8px>'; x += '<div style=margin-bottom:3px><b>' + EscapeHtml(i + (m[0].fqdn ? (', ' + m[0].fqdn) : '')) + '</b></div>'; if (m.desc) { x += addDetailItem("Description", EscapeHtml(m.desc).split('(R)').join('®')); } //if (m.dnssuffix) { x += addDetailItem("DNS Suffix", m.dnssuffix); } if (typeof m[0].mac == 'string') { if (m[0].gatewaymac) { x += addDetailItem("MAC Layer", format("MAC: {0}, Gateway: {1}", EscapeHtml(m[0].mac), EscapeHtml(m[0].gatewaymac))); } else { x += addDetailItem("MAC Layer", format("MAC: {0}", EscapeHtml(m[0].mac))); } } for (var j = 0; j < m.length; j++) { var iplayer = m[j]; if (iplayer.family == 'IPv4') { if (iplayer.gateway && iplayer.netmask) { x += addDetailItem("IPv4 Layer", format("{0}, Mask: {1}, Gateway: {2}", EscapeHtml(iplayer.address), EscapeHtml(iplayer.netmask), EscapeHtml(iplayer.gateway))); } else { if (iplayer.address) { x += addDetailItem("IPv4 Layer", format("{0}", EscapeHtml(iplayer.address))); } } } if (iplayer.family == 'IPv6') { if (iplayer.gateway && iplayer.netmask) { x += addDetailItem("IPv6 Layer", format("{0}, Mask: {1}, Gateway: {2}", EscapeHtml(iplayer.address), EscapeHtml(iplayer.netmask), EscapeHtml(iplayer.gateway))); } else { if (iplayer.address) { x += addDetailItem("IPv6 Layer", format("{0}", EscapeHtml(iplayer.address))); } } } } x += '</div>'; } x += '</table>'; if (x != '') { sections.push({ name: "Networking", html: x, img: 'networking' }); } } // Attribute: Intel AMT if (node.intelamt != null) { var x = ''; x += addDetailItem("Version", (node.intelamt.ver) ? ('v' + EscapeHtml(node.intelamt.ver)) : ('<i>' + "Unknown" + '</i>'), s); var provisioningStates = { 0: nobreak("Not Activated (Pre)"), 1: nobreak("Not Activated (In)"), 2: nobreak("Activated") }; var provisioningMode = ''; if ((node.intelamt.state == 2) && node.intelamt.flags) { if (node.intelamt.flags & 2) { provisioningMode = (', ' + "Client Control Mode (CCM)"); } else if (node.intelamt.flags & 4) { provisioningMode = (', ' + "Admin Control Mode (ACM)"); } } x += addDetailItem("Provisioning State", ((node.intelamt.state) ? (provisioningStates[node.intelamt.state]) : ('<i>' + "Unknown" + '</i>')) + provisioningMode, s); x += addDetailItem("Security", (node.intelamt.tls == 1) ? "Secured using TLS" : "TLS is not setup", s); // Check that the Intel AMT user is setup and there is no warnings (1 = invalid credentials, 8 = trying) x += addDetailItem("Admin Credentials", ((node.intelamt.user) == null || (node.intelamt.user == '') || ((node.intelamt.warn != null) && ((node.intelamt.warn & 9) != 0))) ? "Not Known" : "Known", s); if (x != '') { sections.push({ name: "Intel® Active Management Technology (Intel® AMT)", html: x, img: 'amt' }); } } if (hardware.identifiers) { var x = '', ident = hardware.identifiers; // BIOS if (ident.bios_vendor) { x += addDetailItem("Vendor", EscapeHtml(ident.bios_vendor), s); } if (ident.bios_version) { x += addDetailItem("Version", EscapeHtml(ident.bios_version), s); } if (x != '') { sections.push({ name: "BIOS", html: x, img: 'chip' }); } // Motherboard x = ''; if (ident.board_vendor) { x += addDetailItem("Vendor", EscapeHtml(ident.board_vendor), s); } if (ident.board_name) { x += addDetailItem("Name", EscapeHtml(ident.board_name), s); } if (ident.board_serial && (ident.board_serial != '')) { x += addDetailItem("Serial", EscapeHtml(ident.board_serial), s); } if (ident.board_version) { x += addDetailItem("Version", EscapeHtml(ident.board_version), s); } if (ident.product_uuid) { x += addDetailItem("Identifier", EscapeHtml(ident.product_uuid), s); } if (ident.cpu_name) { x += addDetailItem("CPU", EscapeHtml(ident.cpu_name).split('(TM)').join('™').split('(R)').join('®'), s); } if (ident.gpu_name) { for (var i in ident.gpu_name) { x += addDetailItem("GPU", EscapeHtml(ident.gpu_name[i]).split('(TM)').join('™').split('(R)').join('®'), s); } } if (x != '') { sections.push({ name: "Motherboard", html: x, img: 'motherboard' }); } } if (hardware.windows) { if (hardware.windows.memory && (hardware.windows.memory.length > 0)) { var x = ''; // Sort Memory hardware.windows.memory.sort(function (a, b) { if (a.BankLabel > b.BankLabel) return 1; if (a.BankLabel < b.BankLabel) return -1; return 0; }); x += '<table style=width:100%>'; for (var i in hardware.windows.memory) { var m = hardware.windows.memory[i]; x += '<tr><td><div class=style10 style=border-radius:5px;padding:8px>'; x += '<div style=margin-bottom:3px><b>' + EscapeHtml(m.BankLabel) + '</b></div>'; if (m.Capacity && m.Speed) { x += addDetailItem("Capacity / Speed", format("{0} Mb, {1} Mhz", (m.Capacity / 1024 / 1024), m.Speed), s); } else if (m.Capacity) { x += addDetailItem("Capacity", format("{0} Mb", (m.Capacity / 1024 / 1024)), s); } if (m.PartNumber) { x += addDetailItem("Part Number", EscapeHtml((m.Manufacturer && m.Manufacturer != 'Undefined') ? (m.Manufacturer + ', ') : '') + EscapeHtml(m.PartNumber), s); } x += '</div>'; } x += '</table>'; if (x != '') { sections.push({ name: "Memory", html: x, img: 'ram' }); } } } // Storage if (hardware.identifiers && ident.storage_devices) { var x = ''; // Sort Storage ident.storage_devices.sort(function (a, b) { if (a.Caption > b.Caption) return 1; if (a.Caption < b.Caption) return -1; return 0; }); x += '<table style=width:100%>'; for (var i in ident.storage_devices) { var m = ident.storage_devices[i]; if (m.Size) { x += '<tr><td><div class=style10 style=border-radius:5px;padding:8px>'; x += '<div style=margin-bottom:3px><b>' + EscapeHtml(m.Caption) + '</b></div>'; if (m.Model && (m.Model != m.Caption)) { x += addDetailItem("Model", EscapeHtml(m.Model), s); } if (m.Size) { if ((typeof m.Size == 'string') && (parseInt(m.Size) == m.Size)) { m.Size = parseInt(m.Size); } if (typeof m.Size == 'number') { x += addDetailItem("Capacity", format("{0} Mb", Math.floor(m.Size / 1024 / 1024)), s); } if (typeof m.Size == 'string') { x += addDetailItem("Capacity", EscapeHtml(m.Size), s); } } x += '</div>'; } } x += '</table>'; if (x != '') { sections.push({ name: "Storage", html: x, img: 'storage' }); } } // Render the sections var x = ''; for (var i in sections) { if (sections[i].img == null) { x += '<div class=DevSt style=margin-bottom:3px;margin-left:4px><b>' + sections[i].name + '</b></div><div style=margin-bottom:10px;margin-left:4px>' + sections[i].html + '</div>'; } else { x += '<table style=width:100%><tr>'; x += '<td style=width:32px;vertical-align:top><img src=images/details/' + sections[i].img + '32.png srcset="images/details/' + sections[i].img + '64.png 2x" border=0 width=32 /></td>'; // height=12 x += '<td><div class=DevSt style=margin-bottom:3px;margin-left:4px><b>' + sections[i].name + '</b></div><div style=margin-bottom:10px;margin-left:4px>' + sections[i].html + '</div></td>'; x += '</tr></table>'; } } if (x == '') { QH('p10detailshtml', "No information for this device."); } else { QH('p10detailshtml', x); } } // // CONSOLE // /* function agentConsoleHandleKeys(e) { if ((e.ctrlKey) || (e.altKey)) { return true; } var processed = 0, box = Q('p15consoleText'); if (e.key) { if (e.keyCode == 13 && consoleFocus == 0) { p15consoleSend(e); processed = 1; } else if (e.keyCode == 8 && consoleFocus == 0) { var x = box.value; box.value = x.substring(0, x.length - 1); processed = 1; } else if (e.keyCode == 27) { box.value = ''; processed = 1; } else if ((e.keyCode == 38) || (e.keyCode == 40)) { // Arrow up || Arrow down var hindex = consoleHistory.indexOf(box.value); //console.log(hindex, consoleHistory); if ((e.keyCode == 38) && ((consoleHistory.length - 1) > hindex)) { box.value = consoleHistory[hindex + 1]; } else if ((e.keyCode == 40) && (hindex > 0)) { box.value = consoleHistory[hindex - 1]; } else if ((e.keyCode == 40) && (hindex == 0)) { box.value = ''; } processed = 1; } else if (e.key.length === 1) { //box.value = ((box.value + e.key)); insertTextAtCursor(box, e.key); processed = 1; } } else { if (e.charCode != 0 && consoleFocus == 0) { box.value = ((box.value + String.fromCharCode(e.charCode))); processed = 1; } } if (processed > 0) { return haltEvent(e); } } */ // Insert text at the cursor location on the function insertTextAtCursor(ctrl, val) { if (document.selection) { ctrl.focus(); sel = document.selection.createRange(); sel.text = val; } else if (ctrl.selectionStart || ctrl.selectionStart == '0') { var start = ctrl.selectionStart, end = ctrl.selectionEnd; ctrl.value = ctrl.value.substring(0, start) + val + ctrl.value.substring(end, ctrl.value.length); ctrl.setSelectionRange(end + 1, end + 1); } else { ctrl.value += myValue; } } var consoleNode; var consoleServerText = ''; function setupConsole() { // Setup the console var samenode = (consoleNode == currentNode); consoleNode = currentNode; var mesh = meshes[consoleNode.meshid]; var rights = GetNodeRights(currentNode); if ((rights & 16) != 0) { if (consoleNode.consoleText == null) { consoleNode.consoleText = ''; } if (samenode == false) { QH('p15agentConsoleText', consoleNode.consoleText); Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight; } var online = (((consoleNode.conn & 1) != 0) || ((consoleNode.conn & 16) != 0)) ? true : false; var onlineText = ((consoleNode.conn & 1) != 0) ? "Agent is online" : "Agent is offline" if ((consoleNode.conn & 16) != 0) { onlineText += ", MQTT is online" } QH('p15statetext', onlineText); QE('p15uploadCore', ((consoleNode.conn & 1) != 0)); QV('p15outputselecttd', ((consoleNode.conn & 16) != 0) || ((currentNode.pmt == 1) && ((features2 & 2) != 0))); QV('p15outputselect2', ((consoleNode.conn & 16) != 0)); // MQTT channel QV('p15outputselect3', ((currentNode.pmt == 1) && ((features2 & 2) != 0))); // Push Notification channel var c = Q('p15outputselect').value; if (((consoleNode.conn & 16) == 0) && (c == 2)) { c = 1; Q('p15outputselect').value = 1; } if (((currentNode.pmt != 1) || ((features2 & 2) == 0)) && (c == 3)) { c = 1; Q('p15outputselect').value = 1; } var active = false; if (((consoleNode.conn & 1) != 0) && (c == 1)) { active = true; } // Agent if (((consoleNode.conn & 16) != 0) && (c == 2)) { active = true; } // MQTT if (((currentNode.pmt == 1) && ((features2 & 2) != 0)) && (c == 3)) { active = true; } // Push QE('p15consoleText', active); } else { QH('p15statetext', "Access Denied"); QE('p15consoleText', false); QE('p15uploadCore', false); QV('p15outputselecttd', false); } QV('devListToolbarViewIcons3', ((consoleNode.conn & 1) != 0)); } // Clear the console for this node function p15consoleClear() { QH('p15agentConsoleText', ''); Q('id_p15consoleClear').blur(); consoleNode.consoleText = ''; } // Send a command to the agent var consoleHistory = []; function p15consoleSend(e) { if (e && e.keyCode != 13) return; var v = Q('p15consoleText').value, t = '<div style=color:green>> ' + EscapeHtml(v) + '<br/></div>'; if (((consoleNode.conn & 16) != 0) && (Q('p15outputselect').value == 2)) { // Send the command to MQTT t = '<div style=color:orange>' + "MQTT" + '> ' + EscapeHtml(v) + '<br/></div>'; consoleNode.consoleText += t; meshserver.send({ action: 'sendmqttmsg', topic: 'console', nodeids: [consoleNode._id], msg: v }); } else if ((consoleNode.pmt == 1) && (Q('p15outputselect').value == 3) && ((features2 & 2) != 0)) { // Send the command using push notification t = '<div style=color:violet>' + "PUSH" + '> ' + EscapeHtml(v) + '<br/></div>'; consoleNode.consoleText += t; meshserver.send({ action: 'pushconsole', nodeid: consoleNode._id, console: v }); } else if ((consoleNode.conn & 1) != 0) { // Send the command to the mesh agent consoleNode.consoleText += t; meshserver.send({ action: 'msg', type: 'console', nodeid: consoleNode._id, value: v }); } Q('p15agentConsoleText').innerHTML += t; Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight; Q('p15consoleText').value = ''; // Add command to history list if (v.length > 0) { // Move this command to the top if it already exists var j = consoleHistory.indexOf(v); if (j >= 0) { consoleHistory.splice(j, 1); } consoleHistory.unshift(v); consoleHistory.splice(10); } } // Handle Mesh Agent console data function p15consoleReceive(node, data, source) { if (node === 'serverconsole') { // Server console data data = '<div>' + EscapeHtml(data) + '</div>' consoleServerText += data; if (consoleNode == 'server') { Q('p15agentConsoleText').innerHTML += data; Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight; } } else { // Agent console data if (source == 'MQTT') { data = '<div style=color:red>' + "MQTT" + '> ' + EscapeHtml(data) + '<br/></div>'; } else { data = '<div>' + EscapeHtml(data) + '</div>' } if (node.consoleText == null) { node.consoleText = data; } else { node.consoleText += data; } if (consoleNode == node) { Q('p15agentConsoleText').innerHTML += data; Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight; } } } // Save console text to file function p15downloadConsoleText() { saveAs(new Blob([Q('p15agentConsoleText').innerText], { type: 'application/octet-stream' }), "console.txt"); } // Called then user presses the "Change Core" button function p15uploadCore(e) { if (xxdialogMode) return; if (e.shiftKey == true) { meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'default' }); } // Upload default core else if (e.altKey == true) { meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'clear' }); } // Clear the core else if (e.ctrlKey == true) { p15uploadCore2(); } // Upload the core from a file else { setDialogMode(2, "Perform Agent Action", 3, p15uploadCoreEx, addHtmlValue("Action", '<select id=d3coreMode style=width:230px><option value=1>' + "Upload default server core" + '</option><option value=2>' + "Clear the core" + '</option><option value=6>' + "Upload recovery core" + '</option><option value=7>' + "Upload tiny core" + '</option><option value=3>' + "Upload a core file" + '</option><option value=4>' + "Soft disconnect agent" + '</option><option value=5>' + "Hard disconnect agent" + '</option></select>')); } } function p15uploadCoreEx() { if (Q('d3coreMode').value == 1) { // Upload default core meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'default' }); } else if (Q('d3coreMode').value == 2) { // Clear the core meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'clear' }); } else if (Q('d3coreMode').value == 3) { // Upload file as core p15uploadCore2(); } else if (Q('d3coreMode').value == 4) { // Soft disconnect the mesh agent meshserver.send({ action: 'agentdisconnect', nodeid: consoleNode._id, disconnectMode: 1 }); } else if (Q('d3coreMode').value == 5) { // Hard disconnect the mesh agent meshserver.send({ action: 'agentdisconnect', nodeid: consoleNode._id, disconnectMode: 2 }); } else if (Q('d3coreMode').value == 6) { // Upload a recovery core meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'recovery' }); } else if (Q('d3coreMode').value == 7) { // Upload a tiny core meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'tiny' }); } } // Called then user opts to upload a file as core function p15uploadCore2() { if (xxdialogMode) return; Q('d3localmodeform').action = 'uploadmeshcorefile.ashx'; Q('d3auth').value = authCookie; Q('d3attrib').value = currentNode._id; setDialogMode(3, "Upload Mesh Agent Core", 3, p15uploadCoreEx2); d3init(); } function p15uploadCoreEx2() { var mode = Q('d3uploadMode').value; if (mode == 1) { // Upload local mesh agent core Q('d3submit').click(); } else { // Upload server mesh agent code var files = d3getFileSel(); if (files.length == 1) { meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'custom', path: d3filetreelocation.join('/') + '/' + files[0] }); } } } // // MY MESHS // var currentMesh; function p20updateMesh() { if (currentMesh == null) return; QH('p20meshName', EscapeHtml(currentMesh.name)); var meshtype = format("Unknown #{0}", currentMesh.mtype); var meshrights = GetMeshRights(currentMesh); if (currentMesh.mtype == 1) meshtype = "Intel® AMT only, no agent"; if (currentMesh.mtype == 2) meshtype = "Managed using a software agent"; var x = ''; x += addHtmlValue("Name", addLinkConditional(EscapeHtml(currentMesh.name), 'p20editmesh(1)', (meshrights & 1) != 0)); x += addHtmlValue("Description", addLinkConditional(((currentMesh.desc && currentMesh.desc != '') ? EscapeHtml(currentMesh.desc) : ('<i>' + "None" + '</i>')), 'p20editmesh(2)', (meshrights & 1) != 0)); x += addHtmlValue("Type", meshtype); //x += addHtmlValue('Identifier', currentMesh._id.split('/')[2]); x += '<br><input type=button value=Notes onclick=showNotes(false,"' + encodeURIComponent(currentMesh._id) + '") />'; x += '<br style=clear:both><br>'; var currentMeshLinks = currentMesh.links[userinfo._id]; if (currentMeshLinks && ((currentMeshLinks.rights & 2) != 0)) { x += '<div style=margin-bottom:6px;float:left;margin-right:10px><a onclick=p20showAddMeshUserDialog() style=cursor:pointer><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Add User" + '</a></div>'; } if (navigator.userAgent.toLowerCase().indexOf('android') >= 0) { x += '<div style=margin-bottom:6px;float:left;margin-right:10px><a onclick=p20installAndroidDialog() style=cursor:pointer><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Install on this device" + '</a></div>'; } /* if ((meshrights & 4) != 0) { if (currentMesh.mtype == 1) { x += '<a onclick=addCiraDeviceToMesh("' + currentMesh._id + '") style=cursor:pointer;margin-right:10px><img src=images/icon-installmesh.png border=0 height=12 width=12> Install CIRA</a>'; x += '<a onclick=addDeviceToMesh("' + currentMesh._id + '") style=cursor:pointer;margin-right:10px><img src=images/icon-installmesh.png border=0 height=12 width=12> Install local</a>'; } if (currentMesh.mtype == 2) { x += '<a onclick=addAgentToMesh("' + currentMesh._id + '") style=cursor:pointer;margin-right:10px><img src=images/icon-addnew.png border=0 height=12 width=12> Install</a>'; } } */ /* function getMeshActions(mesh, meshrights) { if ((meshrights & 4) == 0) return ''; var r = ''; if (mesh.mtype == 1) { r += ' <a style=cursor:pointer;font-size:10px onclick=addCiraDeviceToMesh("' + mesh._id + '")>Add CIRA</a>'; r += ' <a style=cursor:pointer;font-size:10px onclick=addDeviceToMesh("' + mesh._id + '")>Add Local</a>'; } if (mesh.mtype == 2) { r += ' <a style=cursor:pointer;font-size:10px onclick=addAgentToMesh("' + mesh._id + '")>Add Agent</a>'; } return r; } */ x += '<table style="color:black;background-color:#EEE;border-color:#AAA;border-width:1px;border-style:solid;border-collapse:collapse" border=0 cellpadding=2 cellspacing=0 width=100%><tbody><tr style=background-color:#AAAAAA;font-weight:bold><th scope=col style=text-align:left;width:430px>' + "User Authorizations" + '</th></tr>'; // Sort the users for this mesh var count = 1, sortedusers = []; for (var i in currentMesh.links) { var uname = i.split('/')[2]; if (currentMesh.links[i].name) { uname = currentMesh.links[i].name; } if (i == userinfo._id) { uname = userinfo.name; } if ((usergroups != null) && (usergroups[i] != null)) { uname = usergroups[i].name; } sortedusers.push({ id: i, name: uname, rights: currentMesh.links[i].rights }); } sortedusers.sort(function (a, b) { if (a.name > b.name) return 1; if (a.name < b.name) return -1; return 0; }); // Display all users for this mesh for (var i in sortedusers) { var trash = '', rights = "Partial Rights", r = sortedusers[i].rights, icon = 2; if (r == 0xFFFFFFFF) rights = "Full Administrator"; else if (r == 0) rights = "No Rights"; if ((i != userinfo._id) && (meshrights == 0xFFFFFFFF || (((meshrights & 2) != 0)))) { trash = '<a onclick=p20deleteUser(event,"' + encodeURIComponent(sortedusers[i].id) + '") style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; } if (sortedusers[i].id.startsWith('ugrp/')) { icon = 4; } x += '<tr onclick=p20viewuser("' + encodeURIComponent(sortedusers[i].id) + '") style=height:32px;cursor:pointer' + (((count % 2) == 0) ? ';background-color:#DDD' : '') + '><td>'; x += '<div style=float:right>' + trash + '</div><div style=float:right;padding-right:4px>' + rights + '</div><div class=m' + icon + '></div><div> ' + EscapeHtml(decodeURIComponent(sortedusers[i].name)) + '<div></div></div>'; x += '</td></tr>'; ++count; } x += '</tbody></table>'; // If we are full administrator on this mesh, allow deletion of the mesh if (meshrights == 0xFFFFFFFF) { x += '<div style=font-size:small;text-align:right;margin-top:6px><span><a onclick=p20showDeleteMeshDialog() style=cursor:pointer>' + "Delete Group" + '</a></span></div>'; } QH('p20info', x); } function p20showDeleteMeshDialog() { if (xxdialogMode) return false; var x = format("Are you sure you want to delete group {0}? Deleting the device group will also delete all information about devices within this group.", EscapeHtml(currentMesh.name)) + '<br /><br />'; x += '<label><input id=p20check type=checkbox onchange=p20validateDeleteMeshDialog() />' + "Confirm" + '</label>'; setDialogMode(2, "Delete Group", 3, p20showDeleteMeshDialogEx, x); p20validateDeleteMeshDialog(); return false; } function p20validateDeleteMeshDialog() { QE('idx_dlgOkButton', Q('p20check').checked); } function p20showDeleteMeshDialogEx(buttons, tag) { meshserver.send({ action: 'deletemesh', meshid: currentMesh._id, meshname: currentMesh.name }); } function p20editmesh(focus) { if (xxdialogMode) return; var x = addHtmlValue("Name", '<input id=dp20meshname style=width:170px maxlength=32 onchange=p20editmeshValidate() onkeyup=p20editmeshValidate() />'); x += addHtmlValue("Description", '<input id=dp20meshdesc style=width:170px maxlength=1024 onkeyup=p20editmeshValidate() />'); setDialogMode(2, "Edit Device Group", 3, p20editmeshEx, x); Q('dp20meshname').value = currentMesh.name; if (currentMesh.desc) Q('dp20meshdesc').value = currentMesh.desc; p20editmeshValidate(); if (focus == 2) { Q('dp20meshdesc').focus(); } else { Q('dp20meshname').focus(); } } function p20editmeshEx() { meshserver.send({ action: 'editmesh', meshid: currentMesh._id, meshname: Q('dp20meshname').value, desc: Q('dp20meshdesc').value }); } function p20editmeshValidate() { QE('idx_dlgOkButton', Q('dp20meshname').value.length > 0); } function p20installAndroidDialog() { if (xxdialogMode) return; var x = '<div>' + "Install the MeshCentral Agent on your Android device. Once installed, click the pairing link to connect your device to this server." + '</div>'; x += '<br /><div><a href="https://play.google.com/store/apps/details?id=com.meshcentral.agent2"><b>' + "MeshCentral Agent for Android" + '</b></a><div>'; x += '<br /><div><a href="' + serverinfo.magenturl + ',' + serverinfo.agentCertHash + ',' + currentMesh._id.split('/')[2] + '"><b>' + "Device Pairing Link" + '</b></a><div>'; setDialogMode(2, "Android Installation", 1, null, x); } function p20showAddMeshUserDialog() { if (xxdialogMode) return; var x = addHtmlValue('User ID', '<input id=dp20username style=width:170px maxlength=256 onchange=p20validateAddMeshUserDialog() onkeyup=p20validateAddMeshUserDialog() />'); x += '<div style="border:2px groove gray;background-color:white;max-height:120px;overflow-y:scroll">'; x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20fulladmin>' + "Full Administrator" + '</label><br>'; x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20editmesh>' + "Edit Device Group" + '</label><br>'; x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20manageusers>' + "Manage Device Group Users" + '</label><br>'; x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20managecomputers>' + "Manage Device Group Computers" + '</label><br>'; x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20remotecontrol>' + "Remote Control" + '</label><br>'; x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20remoteview style=margin-left:12px>' + "Remote View Only" + '</label><br>'; x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20remotelimitedinput style=margin-left:12px>' + "Limited Input Only" + '</label><br>'; x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20noterminal style=margin-left:12px>' + "No Terminal Access" + '</label><br>'; x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20nofiles style=margin-left:12px>' + "No File Access" + '</label><br>'; x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20noamt style=margin-left:12px>' + "No Intel® AMT" + '</label><br>'; x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20meshagentconsole>' + "Mesh Agent Console" + '</label><br>'; x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20meshserverfiles>' + "Server Files" + '</label><br>'; x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20wakedevices>' + "Wake Devices" + '</label><br>'; x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20editnotes>' + "Edit Device Notes" + '</label><br>'; x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20limitevents>' + "Show Only Own Events" + '</label><br>'; x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20chatnotify>' + "Chat & Notify" + '</label><br>'; x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20uninstall>' + "Uninstall Agent" + '</label><br>'; x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20commands>' + "Remote Commands" + '</label><br>'; x += '<label><input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20resetoff>' + "Reset / Power Off" + '</label><br>'; x += '</div>'; setDialogMode(2, "Add User to Device Group", 3, p20showAddMeshUserDialogEx, x); p20validateAddMeshUserDialog(); Q('dp20username').focus(); } function p20validateAddMeshUserDialog() { var meshrights = GetMeshRights(currentMesh); var nc = !Q('p20fulladmin').checked; QE('p20fulladmin', meshrights == 0xFFFFFFFF); QE('p20editmesh', nc && (meshrights == 0xFFFFFFFF)); QE('p20manageusers', nc); QE('p20managecomputers', nc); QE('p20remotecontrol', nc); QE('p20meshagentconsole', nc); QE('p20meshserverfiles', nc); QE('p20wakedevices', nc); QE('p20editnotes', nc); QE('p20limitevents', nc); QE('p20remoteview', nc && Q('p20remotecontrol').checked); QE('p20remotelimitedinput', nc && Q('p20remotecontrol').checked && !Q('p20remoteview').checked); QE('p20noterminal', nc && Q('p20remotecontrol').checked); QE('p20nofiles', nc && Q('p20remotecontrol').checked); QE('p20noamt', nc && Q('p20remotecontrol').checked); QE('p20chatnotify', nc); QE('p20uninstall', nc); QE('p20commands', nc); QE('p20resetoff', nc); } function p20showAddMeshUserDialogEx() { var meshadmin = 0; if (Q('p20fulladmin').checked == true) { meshadmin = 0xFFFFFFFF; } else { if (Q('p20editmesh').checked == true) meshadmin += 1; if (Q('p20manageusers').checked == true) meshadmin += 2; if (Q('p20managecomputers').checked == true) meshadmin += 4; if (Q('p20remotecontrol').checked == true) meshadmin += 8; if (Q('p20meshagentconsole').checked == true) meshadmin += 16; if (Q('p20meshserverfiles').checked == true) meshadmin += 32; if (Q('p20wakedevices').checked == true) meshadmin += 64; if (Q('p20editnotes').checked == true) meshadmin += 128; if (Q('p20remoteview').checked == true) meshadmin += 256; if (Q('p20noterminal').checked == true) meshadmin += 512; if (Q('p20nofiles').checked == true) meshadmin += 1024; if (Q('p20noamt').checked == true) meshadmin += 2048; if (Q('p20remotelimitedinput').checked == true) meshadmin += 4096; if (Q('p20limitevents').checked == true) meshadmin += 8192; if (Q('p20chatnotify').checked == true) meshadmin += 16384; if (Q('p20uninstall').checked == true) meshadmin += 32768; if (Q('p20commands').checked == true) meshadmin += 131072; if (Q('p20resetoff').checked == true) meshadmin += 262144; } var users = Q('dp20username').value.split(','), users2 = []; for (var i in users) { users2.push(users[i].trim()); } meshserver.send({ action: 'addmeshuser', meshid: currentMesh._id, meshname: currentMesh.name, usernames: users2, meshadmin: meshadmin }); } function p20viewuser(userid) { if (xxdialogMode) return; userid = decodeURIComponent(userid); var r = [], cmeshrights = GetMeshRights(currentMesh), meshrights = GetMeshRights(currentMesh, userid); if (meshrights == 0xFFFFFFFF) r.push("Full Administrator"); else { if ((meshrights & 1) != 0) r.push("Edit Device Group"); if ((meshrights & 2) != 0) r.push("Manage Device Group Users"); if ((meshrights & 4) != 0) r.push("Manage Device Group Computers"); if ((meshrights & 8) != 0) r.push("Remote Control"); if ((meshrights & 16) != 0) r.push("Agent Console"); if ((meshrights & 32) != 0) r.push("Server Files"); if ((meshrights & 64) != 0) r.push("Wake Devices"); if ((meshrights & 128) != 0) r.push("Edit Notes"); if ((meshrights & 256) != 0) r.push("Remote View Only"); if ((meshrights & 512) != 0) r.push("No Terminal"); if ((meshrights & 1024) != 0) r.push("No Files"); if ((meshrights & 2048) != 0) r.push("No Intel® AMT"); if (((meshrights & 8) != 0) && ((meshrights & 4096) != 0) && ((meshrights & 256) == 0)) r.push("Limited Input"); if ((meshrights & 8192) != 0) r.push("Self Events Only"); if ((meshrights & 16384) != 0) r.push("Chat & Notify"); if ((meshrights & 32768) != 0) r.push("Uninstall"); if ((meshrights & 131072) != 0) r.push("Commands"); if ((meshrights & 262144) != 0) r.push("Reset/Off"); } if (r.length == 0) { r.push("No Rights"); } var buttons = 1, uname = userid.split('/')[2]; if (currentMesh.links[userid].name) { uname = currentMesh.links[userid].name; } var x = addHtmlValue("User Name", EscapeHtml(uname)); if (uname != userid.split('/')[2]) { x += addHtmlValue("User ID", EscapeHtml(userid.split('/')[2])); } x += addHtmlValue("Permissions", r.join(", ")); if (((userinfo._id) != userid) && (cmeshrights == 0xFFFFFFFF || (((cmeshrights & 2) != 0) && (meshrights != 0xFFFFFFFF)))) buttons += 4; setDialogMode(2, "Device Group User", buttons, p20viewuserEx, x, userid); } function p20viewuserEx(button, userid) { if (button != 2) return; var uname = userid.split('/')[2]; if (users && users[userid]) { uname = users[userid].name; } if (usergroups && usergroups[userid]) { uname = usergroups[userid].name; } if (userinfo._id == userid) { uname = userinfo.name; } setDialogMode(2, "Remote Mesh User", 3, p20viewuserEx2, format("Confirm removal of user {0}?", uname), userid); } function p20deleteUser(e, userid) { haltEvent(e); p20viewuserEx(2, decodeURIComponent(userid)); } function p20viewuserEx2(button, userid) { meshserver.send({ action: 'removemeshuser', meshid: currentMesh._id, meshname: currentMesh.name, userid: userid }); } // // NOTIFICATIONS // var notifications = []; // Toggle showing notifications function clickNotificationIcon(show) { //addNotification({ icon:0, text:'test' }); if (show == true) { QV('notifiyBox', true); } else if (show == false) { QV('notifiyBox', false); } else { QV('notifiyBox', QS('notifiyBox')['display'] == 'none'); } drawNotifications(); } // Set the notification count on the upper right oft he screen function setNotificationCount(c) { if (parseInt(Q('notificationCount').innerHTML) == c) return; // If the count did not change, exit now. QH('notificationCount2', c); QV('notificationCount', c > 0); } // Refresh the notification box function drawNotifications() { var notifySettings = getstore('notifications', 0); var r = ''; if (notifications.length == 0) { r = '<div style=margin:5px>' + "There are currently no notifications" + '</div>'; } else { for (var i in notifications) { var n = notifications[i], t = '', d = new Date(n.time), icon = 0; if (n.title != null) { t = '<b>' + EscapeHtml(n.title) + '</b>: ' } if (n.nodeid != null) { var node = getNodeFromId(n.nodeid); if (node != null) { icon = node.icon; if (notifySettings & 16) { t = '<b>' + EscapeHtml(meshes[node.meshid].name) + ' / ' + EscapeHtml(node.name) + '</b>: '; } else { t = '<b>' + EscapeHtml(node.name) + '</b>: '; } // Display with or without group name } } r += '<div title="' + format("Occured at {0}", printDateTime(d)) + '" id="notifyx' + n.id + '" class=notification style="cursor:pointer;border-top:1px solid ' + ((r == '') ? 'transparent' : 'orange') + '">'; if (icon) { r += '<div class=j' + icon + ' onclick="notificationSelected(' + n.id + ')" style=margin:5px;float:left></div>'; } r += '<div onclick="notificationDelete(' + n.id + ')" class=unselectable title="' + "Clear this notification" + '" style=margin:5px;float:right;color:orange><b>X</b></div><div onclick="notificationSelected(' + n.id + ')" style=margin:5px>' + t + EscapeHtml(n.text) + '</div><div style=margin-left:5px;margin-bottom:5px;color:gray;font-size:10px>' + printDateTime(d) + '</div></div>'; } } var deleteall = ''; if (notifications.length > 1) { deleteall = '<div id="notifyRemoveAll" onclick="deleteAllNotifications()" style="cursor:pointer;border-top:1px solid orange;margin:5px;color:orange;text-align:right;padding-right:3px">' + "Clear all" + '</div>'; } QH('notifiyBox', '<div class=customScroll style="max-height:170px;overflow-y:auto;margin:5px">' + r + deleteall + '</div>'); } // A notification was selected function notificationSelected(id, del) { var j = -1; for (var i in notifications) { if (notifications[i].id == id) { j = i; } } if (j != -1) { notificationSelectedEx(notifications[j], id); if (del && notifications[j]) { if (notifications[j].notification) { notifications[j].notification.close(); delete notifications[j].notification; } notificationDelete(id); } } } function notificationSelectedEx(n, id) { if (n.nodeid != null) { if (n.tag == 'desktop') gotoDevice(n.nodeid, 12); // Desktop else if (n.tag == 'terminal') gotoDevice(n.nodeid, 11); // Terminal else if (n.tag == 'files') gotoDevice(n.nodeid, 13); // Files else if (n.tag == 'intelamt') gotoDevice(n.nodeid, 14); // Intel AMT else if (n.tag == 'console') gotoDevice(n.nodeid, 15); // Files else gotoDevice(n.nodeid, 10); // General } else { if ((n.tag == 'backupcodes') && !xxdialogMode) { account_manageOtp(0); notificationDelete(id); } // 2FA backup codes else if ((n.tag != null) && n.tag.startsWith('meshmessenger/')) { safeNewWindow('/messenger?id=' + n.tag + '&title=' + encodeURIComponentEx(n.username), n.tag.split('/')[2]); notificationDelete(id); } else if (n.url != null) { safeNewWindow(n.url); notificationDelete(id); } } } // Remove one notification function notificationDelete(id) { var j = -1, e = Q('notifyx' + id); if (e != null) { for (var i in notifications) { if (notifications[i].id == id) { j = i; } } if (j != -1) { meshserver.send({ action: 'intersession', subaction: 'removeNotify', id: id }); // Remove the notification in other sessions of the same user. if (notifications[j].notification) { notifications[j].notification.close(); delete notifications[j].notification; } notifications.splice(j, 1); e.parentNode.removeChild(e); setNotificationCount(notifications.length); if (notifications.length == 0) { QV('notifiyBox', false); } if (notifications.length == 1) { QV('notifyRemoveAll', false); } if ((notifications.length > 0) && (j == 0)) { var n = notifications[0]; QS('notifyx' + n.id)['border-top'] = '1px solid transparent'; } } } } // Add a new notification and play the notification sound function addNotification(n) { // Perform message translation var translatedTitles = [ null, "New Account", // 1 "Server Limit", "Security Warning", "Account Settings", "Device Group", "Invite Codes" ]; var translatedMessages = [ null, "Permission denied", // 1 "Invalid username", "Invalid password", "Invalid email", "Invalid domain", "Invalid site permissions", "User already exists", "Unable to add user in this mode", "Validation exception", "Account limit reached.", // 10 "Chat Request, Click here to accept.", "There has been {0} failed login attempts on this account since the last login.", "Failed to change email address, another account already using: {0}.", "Email sent.", "User {0} not found.", "Users {0} not found.", "Error, unable to change to previously used password.", "Error, unable to change to commonly used password.", "Error, password not changed.", "Password changed.", // 20 "Current password not correct.", "Error, invite code \"{0}\" already in use.", "SMS gateway not enabled", "No user management rights", "Invalid SMS message", "No phone number for this user", "SMS succesfuly sent.", "SMS error", "SMS error: {0}" ]; if (typeof n.titleid == 'number') { try { n.title = translatedTitles[n.titleid]; } catch (ex) { } } if (typeof n.msgid == 'number') { try { n.text = translatedMessages[n.msgid]; if (Array.isArray(n.args)) { n.text = format(n.text, n.args[0], n.args[1], n.args[2], n.args[3], n.args[4], n.args[5]); } } catch (ex) { } } // Show notification within the web page. if (n.time == null) { n.time = Date.now(); } if (n.id == null) { n.id = Math.random(); } notifications.unshift(n); setNotificationCount(notifications.length); clickNotificationIcon(true); var notifySettings = getstore('notifications', 0); if (notifySettings & 1) { Q('chimes').play(); } // If web notifications are granted, use it. var notification = null; if (Notification && (Notification.permission == 'granted')) { var text = n.text.split('®').join('').split('<b>').join('').split('</b>').join('').split('<br />').join('\r\n'); // Clean up any HTML codes if (n.nodeid) { var node = getNodeFromId(n.nodeid); if (node) { if (notifySettings & 16) { // Notify with group name notification = new Notification(decodeURIComponent('{{{extitle}}}') + ' - ' + meshes[node.meshid].name + ' - ' + node.name, { tag: n.tag, body: text, icon: '/images/notify/icons128-' + node.icon + '.png' }); } else { notification = new Notification(decodeURIComponent('{{{extitle}}}') + ' - ' + node.name, { tag: n.tag, body: text, icon: '/images/notify/icons128-' + node.icon + '.png' }); } } } else { if (n.icon == null) { n.icon = 0; } var title = n.title; if (title == null) { title = ''; } else { title = ' - ' + n.title; } notification = new Notification(decodeURIComponent('{{{extitle}}}') + title, { tag: n.tag, body: text, icon: '/images/notify/icons128-' + n.icon + '.png' }); } notification.id = n.id; notification.xtag = n.tag; notification.url = n.url; notification.nodeid = n.nodeid; notification.username = n.username; notification.onclick = function (e) { notificationSelected(e.target.id, true); } n.notification = notification; } // If the notification has a max time, setup the timer here. if ((typeof n.maxtime == 'number') && (n.maxtime > 0)) { var trigger = function notifyRemoveTrigger() { notificationDelete(notifyRemoveTrigger.xid); }; trigger.xid = n.id; setTimeout(trigger, n.maxtime * 1000); } } // Remove all notifications function deleteAllNotifications() { notifications = []; setNotificationCount(0); drawNotifications(); QV('notifiyBox', false); } // // PANELS // var xxcurrentView = -1; function go(x) { setSessionActivity(); if (xxdialogMode || xxcurrentView == x) return; updateFooterMenu(); setDialogMode(0); // Edit this line when adding a new screen for (var i = 0; i < 32; i++) { QV('p' + i, i == x); } xxcurrentView = x; updateCurrentUrl(); } // Change the URL function updateCurrentUrl() { if (((features & 0x10000000) == 0) && (xxcurrentView > 0)) { var urlviewmode = ''; if ((xxcurrentView >= 10) && (xxcurrentView <= 19)) { // Device Link if (currentNode != null) { urlviewmode = '?viewmode=' + xxcurrentView + '&gotonode=' + currentNode._id.split('/')[2] + ((currentDevicePanel > 0)?('&panel=' + currentDevicePanel):''); } } else if ((xxcurrentView >= 20) && (xxcurrentView <= 29)) { // Device Group Link if (currentMesh != null) { urlviewmode = '?viewmode=' + xxcurrentView + '&gotomesh=' + currentMesh._id.split('/')[2]; } } else if (xxcurrentView > 1) { urlviewmode = '?viewmode=' + xxcurrentView; } for (var i in urlargs) { urlviewmode += (((urlviewmode == '') ? '?' : '&') + i + '=' + urlargs[i]); } try { window.history.replaceState({}, document.title, window.location.pathname + urlviewmode); } catch (ex) { } } } // // POPUP DIALOG // // undefined = Hidden, 1 = Generic Message var xxdialogMode; var xxdialogFunc; var xxdialogButtons; var xxdialogTag; // Display a dialog box // Parameters: Dialog Mode (0 = none), Dialog Title, Buttons (1 = OK, 2 = Cancel, 3 = OK & Cancel), Call back function(0 = Cancel, 1 = OK), Dialog Content (Mode 2 only) function setDialogMode(x, y, b, f, c, tag) { setSessionActivity(); xxdialogMode = x; xxdialogFunc = f; xxdialogButtons = b; xxdialogTag = tag; QE('idx_dlgOkButton', true); QV('idx_dlgOkButton', b & 1); QV('idx_dlgCancelButton', b & 2); QV('id_dialogclose', (b & 2) || (b & 8)); QV('idx_dlgDeleteButton', b & 4); QV('idx_dlgButtonBar', b & 7); if (y) QH('id_dialogtitle', y); for (var i = 1; i < 24; i++) { QV('dialog' + i, i == x); } // Edit this line when more dialogs are added QV('dialog', x); if (c) { if (x == 2) { QH('id_dialogOptions', c); } else { QH('id_dialogMessage', c); } } } function dialogclose(x) { setSessionActivity(); var f = xxdialogFunc; var b = xxdialogButtons; var t = xxdialogTag; setDialogMode(); if (((b & 8) || x) && f) f(x, t); } // // Access Control Functions // These must match server // // Remove user rights function removeUserRights(rights, userid) { if ((userid != userinfo._id) || (userinfo.removeRights == null)) return rights; var add = 0, substract = 0; if ((userinfo.removeRights & 0x00000008) != 0) { substract += 0x00000008; } // No Remote Control if ((userinfo.removeRights & 0x00010000) != 0) { add += 0x00010000; } // No Desktop if ((userinfo.removeRights & 0x00000100) != 0) { add += 0x00000100; } // Desktop View Only if ((userinfo.removeRights & 0x00000200) != 0) { add += 0x00000200; } // No Terminal if ((userinfo.removeRights & 0x00000400) != 0) { add += 0x00000400; } // No Files if ((userinfo.removeRights & 0x00000010) != 0) { substract += 0x00000010; } // No Console if ((userinfo.removeRights & 0x00008000) != 0) { substract += 0x00008000; } // No Uninstall if ((userinfo.removeRights & 0x00020000) != 0) { substract += 0x00020000; } // No Remote Command if ((userinfo.removeRights & 0x00000040) != 0) { substract += 0x00000040; } // No Wake if ((userinfo.removeRights & 0x00040000) != 0) { substract += 0x00040000; } // No Reset/Off if (rights != 0xFFFFFFFF) { // If not administrator, add and subsctract restrictions rights |= add; rights &= (0xFFFFFFFF - substract); } else { // If administrator for a device group, start with permissions and add and subsctract restrictions rights = 1 + 2 + 4 + 8 + 32 + 64 + 128 + 16384 + 32768 + 131072 + 262144 + 524288 + 1048576; rights |= add; rights &= (0xFFFFFFFF - substract); } return rights; } // Get the right of a user on a given device group function GetMeshRights(mesh, userid) { if (mesh == null) { return 0; } if (userid == null) { userid = userinfo._id; } if (typeof mesh == 'string') { mesh = meshes[mesh] } if ((mesh == null) || (mesh.links == null)) { return 0; } // Check if super user if (serverinfo.manageAllDeviceGroups && (userid == userinfo._id)) return removeUserRights(0xFFFFFFFF, userid); // Check device group link permission var rights = 0, r = mesh.links[userid]; if (r != null) { if (r.rights == 0xFFFFFFFF) { return removeUserRights(0xFFFFFFFF, userid); } // User has full rights thru a device group link, stop here. rights = r.rights; } // Check permissions thru user groups var user = null; if (userid == userinfo._id) { user = userinfo; } else { if (users != null) { user = users[userid]; } } if (user != null) { for (var i in user.links) { if (i.startsWith('ugrp/')) { r = mesh.links[i]; if (r != null) { if (r.rights == 0xFFFFFFFF) { return removeUserRights(0xFFFFFFFF, userid); } // User has full rights thru a user group, stop here. rights |= r.rights; // TODO: Deal with reverse permissions } } } } return removeUserRights(rights, userid); } // Returns true if the user can view the given device group function IsMeshViewable(mesh, userid) { if (mesh == null) { return false; } if (userid == null) { userid = userinfo._id; } if (typeof mesh == 'string') { mesh = meshes[mesh] } if ((mesh == null) || (mesh.links == null)) { return false; } if (mesh.links[userid] != null) { return true; } // User has visilibity thru a direct link // Check if user user if (serverinfo.manageAllDeviceGroups && (userid == userinfo._id)) return true; // Check permissions thru user groups var user = null; if (userid == userinfo._id) { user = userinfo; } else { if (users != null) { user = users[userid]; } } if (user != null) { for (var i in user.links) { if ((i.startsWith('ugrp/')) && (mesh.links[i] != null)) { return true; } // User has visilibity thru a user group } } return false; } // Return the user rights for a given node function GetNodeRights(node, userid) { if (node == null) { return 0; } if (userid == null) { userid = userinfo._id; } if (typeof node == 'string') { node = getNodeFromId(node); if (node == null) { return 0; } } var r = GetMeshRights(node.meshid, userid); if (r == 0xFFFFFFFF) return removeUserRights(r, userid); // Check direct device rights using device data if ((node.links != null) && (node.links[userid] != null)) { r |= node.links[userid].rights; } // TODO: Deal with reverse permissions // Check direct device rights thru user groups if ((node.links != null) && (userinfo.links != null)) { for (var i in node.links) { if (i.startsWith('ugrp/') && (userinfo.links[i] != null) && (node.links[i].rights != null)) { r |= node.links[i].rights; } } } // Check direct device rights using user data /* var user = null; if (userid == userinfo._id) { user = userinfo; } else { if (users != null) { user = users[userid]; } } if ((user != null) && (user.links != null)) { var r2 = user.links[node._id]; if (r2 != null) { if (r2.rights == 0xFFFFFFFF) { return 0xFFFFFFFF; } // User has full rights thru a device link, stop here. r |= r2.rights; // TODO: Deal with reverse permissions } } */ return removeUserRights(r, userid); } // Return true if the device is visible to the user function IsNodeViewable(node, userid) { if (node == null) { return false; } if (userid == null) { userid = userinfo._id; } if (typeof node == 'string') { node = getNodeFromId(node); if (node == null) { return false; } } if (IsMeshViewable(node.meshid, userid)) return true; // Check direct device visibility using device data if ((node.links != null) && (node.links[userid] != null)) { return true; } // Check direct device visibility thru user groups if ((node.links != null) && (userinfo.links != null)) { for (var i in node.links) { if (i.startsWith('ugrp/') && (userinfo.links[i] != null) && (node.links[i].rights != null)) { return true; } } } return false; } // // Generic Methods // function getNodeAmtVersion(node) { if ((node == null) || (node.intelamt == null) || (typeof node.intelamt.ver != 'string')) return 0; var verSplit = node.intelamt.ver.split('.'); if (verSplit.length < 2) return 0; return parseInt(verSplit[0]) + (parseInt(verSplit[1]) / 100); } function putstore(name, val) { try { if ((typeof (localStorage) === 'undefined') || (localStorage.getItem(name) == val)) return; if (val == null) { localStorage.removeItem(name); } else { localStorage.setItem(name, val); } } catch (e) { } if (name[0] != '_') { var s = {}; for (var i = 0, len = localStorage.length; i < len; ++i) { var k = localStorage.key(i); if (k[0] != '_') { s[k] = localStorage.getItem(k); } } meshserver.send({ action: 'userWebState', state: JSON.stringify(s) }); } } function getstore(name, val) { try { if (typeof (localStorage) === 'undefined') return val; var v = localStorage.getItem(name); if ((v == null) || (v == null)) return val; return v; } catch (e) { return val; } } function center() { if (xtermfit) xtermfit.fit(); onDevicesScroll(); QS('dialog').left = ((((getDocWidth() - 300) / 2)) + 'px'); deskAdjust(); if (currentNode != null) { drawDeviceTimeline(); } } function messagebox(t, m) { QH('id_dialogMessage', m); setDialogMode(1, t, 1); } function statusbox(t, m) { QH('id_dialogMessage', m); setDialogMode(1, t); } function getDocWidth() { if (window.innerWidth) return window.innerWidth; if (document.documentElement && document.documentElement.clientWidth && document.documentElement.clientWidth != 0) return document.documentElement.clientWidth; return document.getElementsByTagName('body')[0].clientWidth; } function haltEvent(e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; } function haltReturn(e) { if (e.keyCode == 13) { haltEvent(e); } } function validateEmail(v) { var emailReg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; return emailReg.test(v); } function reload() { window.location.href = window.location.href; } function getNodeFromId(id) { for (var i in nodes) { if (nodes[i]._id == id) return nodes[i]; } return null; } function addHtmlValue(t, v) { return '<table><td style=width:120px>' + t + '<td><b>' + v + '</b></table>'; } function addHtmlValue2(t, v) { return '<div><div style=display:inline-block;float:right>' + v + '</div><div style=display:inline-block>' + t + '</div></div>'; } function addHtmlValue4(t, v) { return '<table style=width:100%><td style=width:120px>' + t + '<td style=text-align:right><b>' + v + '</b></table>'; } function addLink(x, f) { return '<a style=cursor:pointer;text-decoration:none onclick=\'' + f + '\'>♦ ' + x + '</a>'; } function addLinkConditional(x, f, c) { if (c) return addLink(x, f); return x; } function passwordcheck(p) { var re = /(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*()]).{8,}/; return re.test(p); } function getFileSizeStr(size) { if (typeof size != 'number') { size = 0; } if (size == 1) return "1 byte"; return format('{0} bytes', size); } function joinPaths() { var x = []; for (var i in arguments) { var w = arguments[i]; if ((w != null) && (w != '')) { while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); } while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); } x.push(w); } } return x.join('/'); } function focusTextBox(x) { setTimeout(function () { Q(x).selectionStart = Q(x).selectionEnd = 65535; Q(x).focus(); }, 0); } var isFilenameValid = (function () { var x1 = /^[^\\/:\*\?"<>\|]+$/, x2 = /^\./, x3 = /^(nul|prn|con|lpt[0-9]|com[0-9])(\.|$)/i; return function isFilenameValid(fname) { return x1.test(fname) && !x2.test(fname) && !x3.test(fname) && (fname[0] != '.'); } })(); function printDate(d) { return d.toLocaleDateString(args.locale); } function printTime(d) { return d.toLocaleTimeString(args.locale); } function printDateTime(d) { return d.toLocaleString(args.locale); } function format(format) { var args = Array.prototype.slice.call(arguments, 1); return format.replace(/{(\d+)}/g, function (match, number) { return typeof args[number] != 'undefined' ? args[number] : match; }); }; function nobreak(x) { return x.split(' ').join(' '); } function getUserName(userid) { if (users && users[userid] != null) return users[userid].name; return userid.split('/')[2]; } function addDetailItem(title, value, state) { return '<table style=width:100%><td>' + nobreak(title) + '<td style=text-align:right>' + value + '</table>'; } function isPrivateIP(a) { return (a.startsWith('10.') || a.startsWith('172.16.') || a.startsWith('192.168.')); } function encodeURIComponentEx(txt) { return encodeURIComponent(txt).replace(/'/g, '%27'); }; function safeNewWindow(url, target) { var newWindow = window.open(url, target, 'noopener,noreferrer'); if (newWindow) { newWindow.opener = null; } } </script> </body> </html>