MeshCentral/views/default-mobile.handlebars

4422 lines
273 KiB
Handlebars

<!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" />
<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" />
<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/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>
a {
color: #036;
text-decoration: underline;
}
#footer a {
color: #fff;
text-decoration: underline;
}
#footer a:hover {
color: #fff;
text-decoration: none;
}
.i1 {
background: url(../images/icons50.png) 0px 0px;
height: 50px;
width: 50px;
border: none;
}
.i2 {
background: url(../images/icons50.png) -50px 0px;
height: 50px;
width: 50px;
border: none;
}
.i3 {
background: url(../images/icons50.png) -100px 0px;
height: 50px;
width: 50px;
border: none;
}
.i4 {
background: url(../images/icons50.png) -150px 0px;
height: 50px;
width: 50px;
border: none;
}
.i5 {
background: url(../images/icons50.png) -200px 0px;
height: 50px;
width: 50px;
border: none;
}
.i6 {
background: url(../images/icons50.png) -250px 0px;
height: 50px;
width: 50px;
border: none;
}
.i7 {
background: url(../images/icons50.png) -300px 0px;
height: 50px;
width: 50px;
border: none;
}
.i8 {
background: url(../images/icons50.png) -350px 0px;
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; }
.NotifyIconSmall2 { width:24px; height:24px; background: url(../images/notify24.png) -24px 0px; }
.NotifyIconSmall3 { width:24px; height:24px; background: url(../images/notify24.png) -48px 0px; }
.NotifyIconSmall4 { width:24px; height:24px; background: url(../images/notify24.png) -72px 0px; }
.NotifyIconSmall5 { width:24px; height:24px; background: url(../images/notify24.png) -96px 0px; }
.NotifyIconSmall6 { width:24px; height:24px; background: url(../images/notify24.png) -120px 0px; }
.NotifyIconSmall7 { width:24px; height:24px; background: url(../images/notify24.png) -144px 0px; }
.NotifyIconSmall8 { width:24px; height:24px; background: url(../images/notify24.png) -168px 0px; }
.NotifyIconSmall9 { width:24px; height:24px; background: url(../images/notify24.png) -192px 0px; }
.gray {
/*filter: url("data:image/svg+xml;utf8,&lt;svg xmlns=\'http://www.w3.org/2000/svg\'&gt;&lt;filter id=\'grayscale\'&gt;&lt;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\'/&gt;&lt;/filter&gt;&lt;/svg&gt;#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;
background-color: white;
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;
}
.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; }
.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;
}
.devList4 {
padding-left: 12px;
padding-top: 2px;
}
.devList5 {
padding-left: 12px;
padding-top: 3px;
color: gray
}
</style>
</head>
<body onload="if (typeof(startup) !== 'undefined') startup();" style="overflow-y:hidden;margin:0;padding:0;border:0;color:black;font-size:13px;font-family:\'Trebuchet MS\', Arial, Helvetica, sans-serif">
<div id=container>
<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>
<img id="topMenuIcon" class=noselect style="position:absolute;right:0;top:10px;bottom:50px;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:30px" onscroll="onDevicesScroll()" ontouchstart="onDeviceTouch(true)" ontouchend="onDeviceTouch(false)"></div>
<div id=xdevicesBar style="position:absolute;overflow-y:auto;height:30px;left:0;right:0;bottom:0px;background-color:#aaa">
<div style="margin:4px">
<span style="width:20px;display:inline-block;text-align:center;cursor:pointer" onclick=clearSearchInput()><b>X</b></span>
<input id=SearchInput type=text placeholder=Filter onchange=onDeviceSearchChanged(event) onkeyup=onDeviceSearchChanged(event) autocomplete=off />&nbsp;
<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 style="background-color:white;width:10px;height:10px;border-radius:10px 0 0 0;border-right:1px solid white;border-bottom:1px solid white"></div>
</div>
<div style="padding:0;font-size:25px;background-color:#036;width:50px;border-radius:0 0 10px 0;height:36px">&#9664;</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"><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%">
<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>&#x2713;</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>&#x2713;</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>&#x2713;</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>&#x2713;</strong></span></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>
<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 style="background-color:white;width:10px;height:10px;border-radius:10px 0 0 0;border-right:1px solid white;border-bottom:1px solid white"></div>
</div>
<div style="padding:0;font-size:25px;background-color:#036;width:50px;border-radius:0 0 10px 0;height:36px">&#9664;</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"></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>&checkmark;</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>&#10007;</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">&nbsp;<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 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 style="background-color:white;width:10px;height:10px;border-radius:10px 0 0 0;border-right:1px solid white;border-bottom:1px solid white"></div>
</div>
<div style="padding:0;font-size:25px;background-color:#036;width:50px;border-radius:0 0 10px 0;height:36px">&#9664;</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=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>
</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>
<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:25px">
<div style="padding-top:2px;padding-bottom:2px;background:#C0C0C0">
<div style="float:right;text-align:right">
<span id="p14power"></span>&nbsp;
<input id=DeskSoftInput type=text style="width:25px;display:none;opacity:.2" onblur="toggleSoftKeys(0)" onkeypress="return ondeskkeypress(event)" onkeydown="return ondeskkeydown(event)" onkeyup="return ondeskkeyup(event)">
</div>
<div style="margin-left:3px">
<input type=button id=connectbutton1 value="Connect" onclick=connectDesktop(event,1) onkeypress="return false" onkeydown="return false" disabled="disabled">
<input type=button id=connectbutton1h value="HW Connect" onclick=connectDesktop(event,2) onkeypress="return false" onkeydown="return false" disabled="disabled">
<input type=button id=disconnectbutton1 value="Disconnect" onclick=connectDesktop(event,0) onkeypress="return false" onkeydown="return false">
<span id="deskstatus">Disconnected</span>
</div>
</div>
</div>
<div id=deskarea3 style="position:absolute;top:25px;width:100%;height:calc(100% - 50px)">
<div id=deskarea3x style="background:black;text-align:center;height:100%;position:relative">
<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=DeskTools style="position:absolute;width:400px;height:100%;background-color:gray;top:0;right:0;border-left:2px solid lightgray;display:none">
<a id=DeskToolsRefreshButton style="float:right;padding:3px;cursor:pointer" onclick="refreshDeskTools()">Refresh</a>
<div id=DeskToolsBar style="position:absolute;padding:3px;border-radius: 3px 3px 0px 0px;top:5px;left:4px;bottom:26px;background-color:lightgray;cursor:pointer">Processes</div>
<div style="position:absolute;top:26px;left:4px;right:4px;bottom:4px;background-color:lightgray;text-align:left">
<div style="border-bottom:1px solid darkgray;padding:3px"><a style=width:50px;padding-right:5px;float:left;cursor:pointer onclick=sortProcess(0)>PID</a><a style=cursor:pointer onclick=sortProcess(1)>Name</a></div>
<div id="DeskToolsProcesses" style="overflow-y:scroll;position:absolute;top:24px;bottom:0px;width:100%"></div>
</div>
</div>
</div>
</div>
<div id=deskarea4 style="position:absolute;bottom:0px;width:100%;height:25px">
<div style=padding-top:2px;padding-bottom:2px;background:#C0C0C0>
<div style=float:right;text-align:right>
<span id=DeskToastButton><img src='images/icon-notify.png' onclick=deviceToastFunction() height=16 width=16 style=padding-top:2px /></span>&nbsp;
<!--<input id=DeskToolsButton type=button value=Tools onkeypress="return false" onkeydown="return false" onclick="toggleDeskTools()">&nbsp;-->
</div>
<div>
<input id="deskActionsBtn" type=button style="margin-left:3px" onkeypress="return false" onkeydown="return false" value=Actions onclick=deviceActionFunction() />
<input type="button" value="Settings" onkeypress="return false" onkeydown="return false" onclick="showDesktopSettings()">
<input type="button" onkeypress="return false" onkeydown="return false" value="Power Actions..." onclick="showPowerActionDlg()" style="display:none">
<input type="button" id="DeskSpecialKeys" value="Keys" onkeypress="return false" onkeydown="return false" onclick="sendSpecialKeys()">
<input type="button" id="DeskSoftKeys" value="Keyboard" onkeypress="return false" onkeydown="return false" onclick="toggleSoftKeys(1)">
<input type="button" id="DeskScreens" value="Screens" onkeypress="return false" onkeydown="return false" onclick="deskSelectScreens()" style="display:none">
<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=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">
<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>
<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 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"></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="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>&checkmark;</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>&#10007;</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">&nbsp;<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>
<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 style="background-color:white;width:10px;height:10px;border-radius:10px 0 0 0;border-right:1px solid white;border-bottom:1px solid white"></div>
</div>
<div style="padding:0;font-size:25px;background-color:#036;width:50px;border-radius:0 0 10px 0;height:36px">&#9664;</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="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;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>
<div style="margin:3px 0 3px 0">
<select id="d7bitmapquality" style="float:right;width:200px;height:20px" dir="rtl"></select>
<div style="height:20px">Quality</div>
</div>
<div style="margin:3px 0 3px 0">
<select id="d7bitmapscaling" style="float:right;width:200px;height:20px" 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>
<div style="height:20px">Scaling</div>
</div>
<div style="margin:3px 0 3px 0">
<select id="d7framelimiter" style="float:right;width:200px;height:20px" 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>
<div style="height:20px">Rate</div>
</div>
</div>
<div id="d7amtkvm">
<h4 style="width:100%;border-bottom:1px solid gray">Intel&reg; AMT Hardware KVM</h4>
<div style='height:26px'>
<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>
<div>Encoding</div>
</div>
<div style="height:60px">
<div style="float:right;border:1px solid #666;width:200px;height:60px;overflow-y:scroll;background-color:white">
<label><input type="checkbox" id='d7showfocus' />Show Focus Tool</label><br />
<label><input type="checkbox" id='d7showcursor' />Show Local Mouse Cursor</label><br />
</div>
<div>Other</div>
</div>
</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>
</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>
<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'); }
var urlargs = parseUriArgs();
if (urlargs.key && (isAlphaNumeric(urlargs.key) == false)) { delete urlargs.key; }
if (urlargs.locale && (isAlphaNumeric(urlargs.locale) == false)) { delete urlargs.locale; }
var args = urlargs;
var debugLevel = parseInt('{{{debuglevel}}}');
var features = parseInt('{{{features}}}');
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&reg; 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 32bit", "Android x86", "PogoPlug ARM", "Android APK", "Linux Poky x86-32bit", "MacOS 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", "Unknown", "Unknown", "FreeBSD x86-64"];
var files;
var passRequirements = '{{{passRequirements}}}';
if (passRequirements != '') { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); }
var sessionActivity = Date.now();
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);
// Connect to the mesh server
meshserver = MeshServerCreateControl(domainUrl, authCookie);
meshserver.onStateChanged = onStateChanged;
meshserver.onMessage = onMessage;
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)); }
}
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
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' });
if (xxcurrentView < 2) { go(2); }
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;
xdr.onload = function () { reload(); };
xdr.onerror = xdr.ontimeout = function () { setTimeout(serverPoll, 10000); };
xdr.send();
}
function updateSelf() {
var accountSettingsLocked = false;
if (userinfo) { accountSettingsLocked = ((userinfo.siteadmin != 0xFFFFFFFF) && ((userinfo.siteadmin & 1024) != 0)); }
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('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);
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();
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();
updateDevices();
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();
updateDevices();
//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
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 'getnetworkinfo': {
if (currentNode._id != message.nodeid) return;
updateDeviceDetails(getNodeFromId(message.nodeid), null, message);
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) + '&nbsp;' + p.substring(4); } else { x += '<td><strike style=color:#BBB>' + p.substring(0, 4) + '&nbsp;' + 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 '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; }
updateDevices();
updateMeshes();
} else if (webstate.stars != null) {
updateDevices();
if (Q('SearchInput').value == '*') { onSearchInputChanged(); }
}
if (currentNode) { refreshDevice(currentNode._id); }
}
break;
}
case 'accountchange': {
// An account was created or changed
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;
}
//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 };
updateMeshes();
updateDevices();
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; }
}
}
updateMeshes();
updateDevices();
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;
updateDevices();
// 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();
updateDevices();
//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);
updateDevices();
//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.userloc = message.event.node.userloc;
node.rdpport = message.event.node.rdpport;
node.consent = message.event.node.consent;
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.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; }
}
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);
//updateMapMarkers();
updateDevices();
//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():'*';
}
updateDevices();
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);
updateDevices();
}
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; }
updateDevices();
refreshDevice(node._id);
//updateMapMarkers();
}
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 };
//if (message.event.tag != null) { n.tag = message.event.tag; }
//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 '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; }
}
updateDevices();
refreshDevice(message.event.nodeid);
//mainUpdate(4);
//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); }
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;
}
}
//
// 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_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&reg; 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: 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>&#9679;<span>'; } else { r = '<span style=color:red>&#9679;<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 style="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">';
r += '<div><div style=padding-left:12px;padding-top:2px><b>' + EscapeHtml(meshes[i].name) + '</b></div><div style=padding-left:12px;padding-top:3px;color:gray>' + 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 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 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) + '&nbsp;'; }
// 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 + '\'>&nbsp;<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 + '\'>&nbsp;<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("Confim {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);
updateDevices();
}
function onOnlineCheckBox(e) {
putstore('onlineOnly', Q('OnlineCheckBox').checked ? 1 : 0);
onSearchInputChanged();
}
function onDeviceSearchChanged(e) {
onSearchInputChanged();
}
function clearSearchInput() {
Q('SearchInput').value = '';
onSearchInputChanged();
}
function onSearchInputChanged() {
var x = Q('SearchInput').value.toLowerCase().trim(); putstore('_search', Q('SearchInput').value);
var userSearch = null, ipSearch = null, groupSearch = null, tagSearch = 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); }
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 (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 (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; } } }
updateDevices();
}
function ondeskkeypress(e) {
toggleSoftKeys(0);
Q('DeskSoftInput').value = '';
setSessionActivity();
if (desktop && !xxdialogMode && xxcurrentView == 10) {
// Check what keys we are allows to send
if (currentNode != null) {
var meshrights = GetMeshRights(currentNode.meshid);
var inputAllowed = ((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);
}
}
function ondeskkeydown(e) {
toggleSoftKeys(0);
Q('DeskSoftInput').value = '';
setSessionActivity();
if (desktop && !xxdialogMode && xxcurrentView == 10) {
// Check what keys we are allows to send
if (currentNode != null) {
var meshrights = GetMeshRights(currentNode.meshid);
var inputAllowed = ((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);
}
}
function ondeskkeyup(e) {
toggleSoftKeys(0);
Q('DeskSoftInput').value = '';
setSessionActivity();
if (desktop && !xxdialogMode && xxcurrentView == 10) {
// Check what keys we are allows to send
if (currentNode != null) {
var meshrights = GetMeshRights(currentNode.meshid);
var inputAllowed = ((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.handleKeyUp(e);
}
}
// Since the update device call can be quite frequent, we can moderate it and only call it at most 5 times a second.
var updateDevicesTimer = null;
function updateDevices() { if (updateDevicesTimer != null) return; updateDevicesTimer = setTimeout(updateDevicesEx, 200); }
var sort = 0;
var deviceHeaderId = 0;
var deviceHeaderCount;
var deviceHeaders = {};
var showRealNames = false;
var deviceHeaderTotal = 0;
var deviceHeaders = {};
var deviceHeadersTitles = {};
function updateDevicesEx() {
if (updateDevicesTimer != null) { clearTimeout(updateDevicesTimer); updateDevicesTimer = null; }
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&reg; 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++;
var title = EscapeHtml(nodes[i].name);
if (title.length == 0) { title = '<i>' + "None" + '</i>'; }
if ((nodes[i].rname != null) && (nodes[i].rname.length > 0)) { title += ' / ' + EscapeHtml(nodes[i].rname); }
var name = EscapeHtml(nodes[i].name);
if (showRealNames == true && nodes[i].rname != null) name = EscapeHtml(nodes[i].rname);
if (name.length == 0) { name = '<i>' + "None" + '</i>'; }
// Add device notification icons
var devNotify = '', devNotifySub = '';
// This device is "starred"
if (stars[nodes[i]._id] == 1) {
devNotifySub += '<img class=deviceNotifyDotSub src=images/icon-star-notify-16.png width=16 height=16>';
}
// This device has session information
if (nodes[i].sessions != null) {
// Display any agent messages
if (nodes[i].sessions.msg != null) {
devNotifySub += '<div style="width:16;height:16" class=deviceNotifyDotSub>' + Object.keys(nodes[i].sessions.msg).length + '</div>';
}
// Sessions are active
if ((nodes[i].sessions.kvm != null) || (nodes[i].sessions.terminal != null) || (nodes[i].sessions.files != null) || (nodes[i].sessions.tcp != null) || (nodes[i].sessions.udp != null)) {
devNotifySub += '<img class=deviceNotifyDotSub src=images/icon-relay-notify.png width=16 height=16>';
}
// Battery state
if (nodes[i].sessions.battery != null) {
var bat = nodes[i].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 = nodes[i].icon, nodestate = NodeStateStr(nodes[i]);
if ((!nodes[i].conn) || (nodes[i].conn == 0)) { icon += ' gray'; }
r += '<div name=xxdevice class=devList1 onclick=goForward(\'' + nodes[i]._id + '\')><div style=display:none>' + devNotify + '<div class="i' + icon + ' devList2"></div><div class=devList3><div class=devList4><b>' + name + '</b></div><div class=devList5>' + nodestate + '</div></div></div></div>';
// 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&reg; 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) {
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) { clearTimeout(onDevicesScrollnagleTimer); onDevicesScrollnagleTimer = null; }
if (onDevicesTouchActive) return;
onDevicesScrollnagleTimer = setTimeout(onDevicesScrollEx, 50);
}
function onDeviceTouch(x) {
if (onDevicesTouchActive == x) return;
onDevicesTouchActive = x;
if (x == false) onDevicesScrollEx();
}
function onDevicesScrollEx() {
onDevicesScrollnagleTimer = null;
var visibleTop = Q('xdevices').scrollTop - 250, visibleBottom = Q('xdevices').scrollTop + Q('xdevices').clientHeight + 250, devdivs = document.getElementsByName('xxdevice');
for (var i = 0; i < devdivs.length; i++) {
devdivs[i].childNodes[0].style.display = ((devdivs[i].offsetTop >= visibleTop) && (devdivs[i].offsetTop < visibleBottom)) ? null : 'none';
}
}
// Show currently active sessions on this device
function showDeviceSessions() {
if (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);
}
// 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&reg; 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); }
updateDevicesEx();
}
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; }
//
// 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);
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);
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.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; }
x += addDeviceAttribute("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 (node.intelamt.tls == 1) { str += ', <span>' + "TLS" + '</span>'; }
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&reg; ME";
if (typeof node.intelamt.sku == 'number') {
if ((node.intelamt.sku & 8) != 0) { meName = "Intel&reg; AMT"; }
else if ((node.intelamt.sku & 16) != 0) { meName = "Intel&reg; 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&reg; 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&reg; AMT CIRA" + '</span>');
else if ((node.conn & 4) != 0) cstate.push('<span>' + "Intel&reg; 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;margin-right:4px;border-radius:5px">' + EscapeHtml(node.tags[i]) + '</span>'; } }
if ((meshrights & 4) != 0) {
x += addDeviceAttribute("Tags", '<span onclick=showEditNodeValueDialog(3) style=cursor:pointer>' + groupingTags + '</span>');
} else {
x += addDeviceAttribute("Tags", groupingTags);
}
x += '</table><br />';
// Show action button, only show if we have permissions 4, 8, 64
if ((meshrights & 76) != 0) { 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);
// Show node last 7 days timeline
//drawDeviceTimeline();
setupFiles();
// Show bottom buttons
x = '<div style=float:right;font-size:x-small;margin-right:10px>';
if ((meshrights & 4) != 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>&nbsp;';
//if (xxmap != null) x += '<a style=cursor:pointer onclick=p10showNodeLocationDialog("' + node._id + '")>Location</a>&nbsp;';
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 += '<span style=font-size:10px>' + "Mesh Agent" + '</span>'; }
if ((connectivity & 2) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += '<span style=font-size:10px>' + "Intel&reg; AMT connected" + '</span>'; }
else if ((connectivity & 4) != 0) { if (powerstate.length > 0) { powerstate += ', '; } powerstate += '<span style=font-size:10px>' + "Intel&reg; AMT detected" + '</span>'; }
if ((connectivity & 16) != 0) { if (powerstate.length > 0) { powerstate += '<br/>'; } powerstate += '<span style=font-size:12px>' + "MQTT channel connected" + '</span>'; }
QH('MainComputerState', powerstate);
// 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: 'getsysinfo', nodeid: currentNode._id });
meshserver.send({ action: 'getnetworkinfo', nodeid: currentNode._id });
QH('p10detailshtml', '');
}
}
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);
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 != 2) && (currentNode != null) && (meshrights & 8) && ((meshrights == 0xFFFFFFFF) || ((meshrights & 1024) == 0)) && ((currentNode.mtype == 2) && (currentNode.agent.caps & 4))) { menus.push({ n: "Files", f: 'setupDeviceMenu(2)' }); }
if ((currentDevicePanel != 3) && (currentNode != null)) { menus.push({ n: "Details", f: 'setupDeviceMenu(3)' }); }
updateFooterMenu(menus);
}
function deviceActionFunction() {
if (xxdialogMode) return;
var meshrights = GetNodeRights(currentNode);
var x = "Select an operation to perform on this device." + '<br /><br />';
var y = '<select id=d2deviceop style=float:right;width:170px>';
if ((meshrights & 64) != 0) { y += '<option value=100>' + "Wake-up" + '</option>'; } // Wake-up permission
if ((meshrights & 8) != 0) { y += '<option value=4>' + "Sleep" + '</option><option value=3>' + "Reset" + '</option><option value=2>' + "Power off" + '</option>'; } // Remote control permission
y += '</select>';
x += addHtmlValue("Operation", y);
setDialogMode(2, "Device Action", 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 });
}
}
// Look to see if we need to update the device timeline
function updateDeviceTimeline() {
if ((meshserver.State != 2) || (powerTimelineNode == null) || (powerTimelineUpdate == null) || (currentNode == null)) 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() {
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>&nbsp;' + 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() />');
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&reg; 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'; }
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 = '';
meshserver.send({ action: 'changedevice', nodeid: tag.node._id, intelamt: { user: amtuser, pass: amtpass, tls: parseInt(Q('dp10tls').value) } });
tag.node.intelamt.user = amtuser;
tag.node.intelamt.tls = parseInt(Q('dp10tls').value);
if (tag.func) { setTimeout(tag.func, 300); }
}
}
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 });
}
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) />');
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 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 };
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));
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);
}
function connectDesktop(e, contype) {
setSessionActivity();
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 = desktopsettings.showmouse;
desktop.m.onScreenSizeChange = deskAdjust;
desktop.Start(desktopNode._id, 16994, '*', '*', 0);
desktop.contype = 2;
} else {
// 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.onStateChanged = onDesktopStateChange;
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 = deskAdjust;
desktop.Start(desktopNode._id);
desktop.contype = 1;
}
} else {
// Disconnect and clean up the remote desktop
desktop.Stop();
desktopNode = desktop = null;
}
}
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.showfocus = d7showfocus.checked;
desktopsettings.showmouse = d7showcursor.checked;
desktopsettings.quality = d7bitmapquality.value;
desktopsettings.scaling = d7bitmapscaling.value;
desktopsettings.framerate = d7framelimiter.value;
localStorage.setItem('desktopsettings', JSON.stringify(desktopsettings));
applyDesktopSettings();
if (desktop) {
if (desktop.contype == 1) {
if (desktop.State != 0) { desktop.m.SendCompressionLevel(1, desktopsettings.quality, desktopsettings.scaling, desktopsettings.framerate); }
}
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;
d7showfocus.checked = desktopsettings.showfocus;
d7showcursor.checked = desktopsettings.showmouse;
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; }
}
var fullscreen = false;
/*
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);
if (fullscreen) {
QS('container').width = '100%';
QS('container')['border-right'] = '0';
QS('container')['border-left'] = '0';
QS('column_l').padding = '0';
QS('column_l').width = '100%';
} else {
QS('container').width = '960px';
QS('container')['border-right'] = '1px solid #b7b7b7';
QS('container')['border-left'] = '1px solid #b7b7b7';
QS('column_l').padding = '0 15px';
QS('column_l').width = '930px';
toggleFullScreen();
}
deskAdjust();
}
*/
function deskAdjust() {
var x = (Q('DeskParent').clientHeight - Q('Desk').clientHeight) / 2;
if (x < 0) {
var mh = Q('DeskParent').clientHeight, mw = 9999;
if (desktop) { mw = (desktop.m.width / desktop.m.height) * mh; }
QS('Desk')['max-height'] = mh + 'px';
QS('Desk')['max-width'] = mw + 'px';
x = 0;
} else {
QS('Desk')['max-height'] = null;
QS('Desk')['max-width'] = null;
}
QS('Desk')['margin-top'] = x + 'px';
QS('Desk')['margin-bottom'] = x + 'px';
}
// Remote desktop special key combos for Windows
function deskSendKeys() {
if (xxdialogMode || desktop == null || desktop.State != 3) return;
var ks = Q('deskkeys').value;
if (ks == 0) { // WIN+Down arrow
if (desktop.contype == 2) {
desktop.m.sendkey([[0xffe7, 1], [0xff54, 1], [0xff54, 0], [0xffe7, 0]]); // Intel AMT: Meta-left down, Down arrow press, Down arrow release, Meta-left release
} else {
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 0x5B], [desktop.m.KeyAction.DOWN, 40], [desktop.m.KeyAction.UP, 40], [desktop.m.KeyAction.EXUP, 0x5B]]); // Agent: L-Winkey press, Down arrow press, Down arrow release, L-Winkey release
}
} else if (ks == 1) { // WIN+Up arrow
if (desktop.contype == 2) {
desktop.m.sendkey([[0xffe7, 1], [0xff52, 1], [0xff52, 0], [0xffe7, 0]]); // Intel AMT: Meta-left down, Up arrow press, Up arrow release, Meta-left release
} else {
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 0x5B], [desktop.m.KeyAction.DOWN, 38], [desktop.m.KeyAction.UP, 38], [desktop.m.KeyAction.EXUP, 0x5B]]); // MeshAgent: L-Winkey press, Up arrow press, Up arrow release, L-Winkey release
}
} else if (ks == 2) { // WIN+L arrow
if (desktop.contype == 2) {
desktop.m.sendkey([[0xffe7, 1], [0x6c, 1], [0x6c, 0], [0xffe7, 0]]); // Intel AMT: Meta-left down, 'l' press, 'l' release, Meta-left release
} else {
desktop.sendCtrlMsg('{"action":"lock"}');
//desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN,0x5B],[desktop.m.KeyAction.DOWN,76],[desktop.m.KeyAction.UP,76],[desktop.m.KeyAction.EXUP,0x5B]]); // MeshAgent: L-Winkey press, 'L' press, 'L' release, L-Winkey release
//desktop.m.SendKeyMsgKC(desktop.m.KeyAction.EXDOWN, 0x5B);
//desktop.m.SendKeyMsgKC(desktop.m.KeyAction.DOWN, 76);
//desktop.m.SendKeyMsgKC(desktop.m.KeyAction.UP, 76);
//desktop.m.SendKeyMsgKC(desktop.m.KeyAction.EXUP, 0x5B);
}
} else if (ks == 3) { // WIN+M arrow
if (desktop.contype == 2) {
desktop.m.sendkey([[0xffe7, 1], [0x6d, 1], [0x6d, 0], [0xffe7, 0]]); // Intel AMT: Meta-left down, 'm' press, 'm' release, Meta-left release
} else {
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 0x5B], [desktop.m.KeyAction.DOWN, 77], [desktop.m.KeyAction.UP, 77], [desktop.m.KeyAction.EXUP, 0x5B]]); // MeshAgent: L-Winkey press, 'M' press, 'M' release, L-Winkey release
}
} else if (ks == 4) { // Shift+WIN+M arrow
if (desktop.contype == 2) {
desktop.m.sendkey([[0xffe1, 1], [0xffe7, 1], [0x6d, 1], [0x6d, 0], [0xffe7, 0], [0xffe1, 0]]); // Intel AMT: Shift-left down, Meta-left down, 'm' press, 'm' release, Meta-left release, Shift-left release
} else {
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.DOWN, 16], [desktop.m.KeyAction.EXDOWN, 0x5B], [desktop.m.KeyAction.DOWN, 77], [desktop.m.KeyAction.UP, 77], [desktop.m.KeyAction.EXUP, 0x5B], [desktop.m.KeyAction.UP, 16]]); // MeshAgent: L-shift press, L-Winkey press, 'M' press, 'M' release, L-Winkey release, L-shift release
}
} else if (ks == 5) { // WIN
if (desktop.contype == 2) {
desktop.m.sendkey([[0xffe7, 1], [0xffe7, 0]]); // Intel AMT: Meta-left down, Meta-left release
} else {
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 0x5B], [desktop.m.KeyAction.EXUP, 0x5B]]); // MeshAgent: L-Winkey press, L-Winkey release
}
} else if (ks == 6) { // WIN+R
if (desktop.contype == 2) {
desktop.m.sendkey([[0xffe7, 1], [0x72, 1], [0x72, 0], [0xffe7, 0]]); // Intel AMT: Meta-left down, 'r' press, 'r' release, Meta-left release
} else {
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 0x5B], [desktop.m.KeyAction.DOWN, 82], [desktop.m.KeyAction.UP, 82], [desktop.m.KeyAction.EXUP, 0x5B]]); // MeshAgent: L-Winkey press, 'R' press, 'R' release, L-Winkey release
}
} else if (ks == 7) { // ALT-F4
if (desktop.contype == 2) {
desktop.m.sendkey([[0xffe9, 1], [0xffc1, 1], [0xffc1, 0], [0xffe9, 0]]); // Intel AMT: Alt down, 'F4' press, 'F4' release, Alt release
} else {
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 18], [desktop.m.KeyAction.DOWN, 115], [desktop.m.KeyAction.UP, 115], [desktop.m.KeyAction.EXUP, 18]]); // MeshAgent: Alt press, 'F4' press, 'F4' release, Alt release
}
} else if (ks == 8) { // CTRL-W
if (desktop.contype == 2) {
desktop.m.sendkey([[0xffe3, 1], [0x77, 1], [0x77, 0], [0xffe3, 0]]); // Intel AMT: Ctrl down, 'w' press, 'w' release, Ctrl release
} else {
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 17], [desktop.m.KeyAction.DOWN, 87], [desktop.m.KeyAction.UP, 87], [desktop.m.KeyAction.EXUP, 17]]); // MeshAgent: Ctrl press, 'W' press, 'W' release, Ctrl release
}
} else if (ks == 9) { // ALT-TAB
if (desktop.contype == 2) {
desktop.m.sendkey([[0xffe9, 1], [0xff09, 1], [0xff09, 0], [0xffe9, 0]]); // Intel AMT: Alt down, 'TAB' press, 'TAB' release, Alt release
} else {
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.EXDOWN, 18], [desktop.m.KeyAction.DOWN, 9], [desktop.m.KeyAction.UP, 9], [desktop.m.KeyAction.EXUP, 18]]); // MeshAgent: Alt press, 'TAB' press, 'TAB' release, Alt release
}
} else if (ks == 10) { // CTRL-ALT-DEL
desktop.m.sendcad();
} else if (ks == 11) { // TAB
if (desktop.contype == 2) {
desktop.m.sendkey([[0xff09, 1], [0xff09, 0]]); // Intel AMT: 'TAB' press, 'TAB' release
} else {
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.DOWN, 9], [desktop.m.KeyAction.UP, 9]]); // MeshAgent: 'TAB' press, 'TAB' release
}
} else if (ks == 12) { // Shift-F10
if (desktop.contype == 2) {
desktop.m.sendkey([[0xffe1, 1], [0xffc7, 1], [0xffc7, 0], [0xffe1, 0]]); // Intel AMT: Left-Shift Down, F10 down, F10 up, Left-Shift Up
} else {
desktop.m.SendKeyMsgKC([[desktop.m.KeyAction.DOWN, 16], [desktop.m.KeyAction.DOWN, 121], [desktop.m.KeyAction.UP, 121], [desktop.m.KeyAction.UP, 16]]); // MeshAgent: Left-Shift Down, F10 down, F10 up, Left-Shift Up
}
}
}
function sendSpecialKeys() {
if (xxdialogMode || desktop == null || desktop.State != 3) return;
setDialogMode(3, "Special Keys", 3, deskSendKeys);
}
// Send CTRL-ALT-DEL
/*
function sendCAD() {
if (xxdialogMode || desktop == null || desktop.State != 3) return;
desktop.m.sendcad();
}
*/
// Toggle soft keyboard
function toggleSoftKeys(x) {
QV('DeskSoftInput', x == 1);
if (x == 1) { Q('DeskSoftInput').focus(); }
}
// Show process dialogs
function toggleDeskTools() {
setSessionActivity();
if (xxdialogMode) return;
if (QS('DeskTools').display == 'none') {
QV('DeskTools', true);
Q('DeskTools').nodeid = currentNode._id;
refreshDeskTools();
} else {
QV('DeskTools', false);
}
}
// Refresh all of the desktop tool panels
function refreshDeskTools() {
setSessionActivity();
QV('DeskToolsRefreshButton', false);
setTimeout(refreshDeskToolsEx, 500);
meshserver.send({ action: 'msg', type: 'ps', nodeid: currentNode._id });
}
function refreshDeskToolsEx() { QV('DeskToolsRefreshButton', true); }
var deskTools = { sort: 1, msg: null };
function sortProcess(sort) { deskTools.sort = sort; showDeskToolsProcesses(deskTools.msg); }
function sortProcessPid(a, b) { if (a.p > b.p) return 1; if (a.p < b.p) return (-1); return 0; }
function sortProcessName(a, b) { if (a.d > b.d) return 1; if (a.d < b.d) return (-1); return 0; }
function showDeskToolsProcesses(message) {
deskTools.msg = message;
if (message == null) { QH('DeskToolsProcesses', ''); return; }
if (Q('DeskTools').nodeid != message.nodeid) return;
var p = [], processes = null;
try { processes = JSON.parse(message.value); } catch (e) { }
if (processes != null) {
for (var pid in processes) { p.push({ p: parseInt(pid), c: processes[pid].cmd, d: processes[pid].cmd.toLowerCase(), u: processes[pid].user }); }
if (deskTools.sort == 0) { p.sort(sortProcessPid); } else if (deskTools.sort == 1) { p.sort(sortProcessName); }
var x = '';
for (var i in p) { if (p[i].p != 0) { x += '<div class=deskToolsBar><div style=width:50px;float:left;text-align:right;padding-right:5px>' + p[i].p + '</div><a style=float:right;padding-right:5px;cursor:pointer onclick=stopProcess(' + p[i].p + ',"' + p[i].c + '")><img width=10 height=10 src="images/trash.png"></a><div style=float:right;padding-right:5px>' + (p[i].u ? p[i].u : '') + '</div><div>' + p[i].c + '</div></div>'; } }
QH('DeskToolsProcesses', x);
}
}
// 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)) desktop.m.mousedown(e) }
function dmouseup(e) { setSessionActivity(); if ((!xxdialogMode && desktop != null)) desktop.m.mouseup(e) }
function dmousemove(e) { setSessionActivity(); if ((!xxdialogMode && desktop != null)) desktop.m.mousemove(e) }
function dmousewheel(e) { setSessionActivity(); if ((!xxdialogMode && desktop != null) && desktop.m.mousewheel) { 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(); deskAdjust(); } }
function stopProcess(id, name) { setDialogMode(2, "Process Control", 3, stopProcessEx, format("Stop process #{0} \"{1}\"?", id, name), id); return false; }
function stopProcessEx(buttons, tag) { meshserver.send({ action: 'msg', type: 'pskill', nodeid: currentNode._id, value: tag }); setTimeout(refreshDeskTools, 300); }
//
// FILES
//
var filesNode;
function setupFiles() {
// Setup the files tab
var samenode = (filesNode == currentNode);
filesNode = currentNode;
var online = ((filesNode.conn & 1) != 0) ? true : false; // If Agent (1) connected, enable Terminal
QE('p13Connect', online);
if (((samenode == false) || (online == false)) && files) { files.Stop(); files = null; }
}
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; }
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) {
if (!files) {
// Setup a mesh agent files
files = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotFiles), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);
files.attemptWebRTC = attemptWebRTC;
files.onStateChanged = onFilesStateChange;
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);
data = JSON.parse(decode_utf8(data));
if (data.action == 'download') { p13gotDownloadCommand(data); 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 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 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 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 + '\'>&nbsp;<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 + '\'>&nbsp;<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() {
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));
QE('p13DeleteFileButton', (cc > 0) && ((p13filetreelocation.length > 0) || (winAgent == false)));
QE('p13NewFolderButton', ((p13filetreelocation.length > 0) || (winAgent == false)));
QE('p13UploadButton', ((p13filetreelocation.length > 0) || (winAgent == false)));
QE('p13RenameFileButton', (cc == 1) && ((p13filetreelocation.length > 0) || (winAgent == false)));
QE('p13SelectAllButton', tc > 0);
Q('p13SelectAllButton').value = (cc > 0 ? "None" : "All");
QE('p13RefreshButton', true);
QE('p13CutButton', (cc > 0) && (cc == sfc) && ((p13filetreelocation.length > 0) || (winAgent == false)));
QE('p13CopyButton', (cc > 0) && (cc == sfc) && ((p13filetreelocation.length > 0) || (winAgent == false)));
QE('p13PasteButton', ((p13filetreelocation.length > 0) || (winAgent == false)) && ((p13clipboard != null) && (p13clipboard.length > 0)));
}
}
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));
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) { x += addDetailItem("Architecture", EscapeHtml(m.OSArchitecture), s); }
}
if (x != '') { sections.push({ name: "Operating System", html: x, img: 'software32.png' }); }
}
// 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; }
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 (x != '') { sections.push({ name: "Mesh Agent", html: x, img: 'meshagent32.png' }); }
}
// 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('&reg;')); }
//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("IP: {0}, Mask: {1}, Gateway: {2}", EscapeHtml(iplayer.address), EscapeHtml(iplayer.netmask), EscapeHtml(iplayer.gateway)));
} else {
x += addDetailItem("IPv4 Layer", format("IP: {0}", EscapeHtml(iplayer.address)));
}
}
if (iplayer.family == 'IPv6') {
if (iplayer.gateway && iplayer.netmask) {
x += addDetailItem("IPv6 Layer", format("IP: {0}, Mask: {1}, Gateway: {2}", EscapeHtml(iplayer.address), EscapeHtml(iplayer.netmask), EscapeHtml(iplayer.gateway)));
} else {
x += addDetailItem("IPv6 Layer", format("IP: {0}", EscapeHtml(iplayer.address)));
}
}
}
x += '</div>';
}
x += '</table>';
if (x != '') { sections.push({ name: "Networking", html: x, img: 'networking64.png' }); }
}
// 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);
x += addDetailItem("Admin Credentials", (node.intelamt.user == null || node.intelamt.user == '') ? "Not Known" : "Known", s);
if (x != '') { sections.push({ name: "Intel&reg; Active Management Technology (Intel&reg; AMT)", html: x, img: 'amt32.png' }); }
}
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: 'chip32.png' }); }
// 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('&trade;').split('(R)').join('&reg;'), s); }
if (ident.gpu_name) { for (var i in ident.gpu_name) { x += addDetailItem("GPU", EscapeHtml(ident.gpu_name[i]).split('(TM)').join('&trade;').split('(R)').join('&reg;'), s); } }
if (x != '') { sections.push({ name: "Motherboard", html: x, img: 'motherboard32.png' }); }
}
if (hardware.windows) {
if (hardware.windows.memory) {
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) { x += addDetailItem("Capacity / Speed", format("{0} Mb, {1} Mhz", (m.Capacity / 1024 / 1024), m.Speed), 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: 'ram32.png' }); }
}
}
// 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: 'storage32.png' }); }
}
// 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 + ' 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);
}
}
//
// 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&reg; 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><a onclick=p20showAddMeshUserDialog() style=cursor:pointer><img src=images/icon-addnew.png border=0 height=12 width=12> ' + "Add User" + '</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>&nbsp;' + 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 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&reg; 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 += '</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);
}
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;
}
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&reg; 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 (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 }); }
//
// 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;
}
//
// 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_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
//
// 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 (userinfo.manageAllDeviceGroups && (userid == userinfo._id)) return 0xFFFFFFFF;
// Check device group link permission
var rights = 0, r = mesh.links[userid];
if (r != null) {
if (r.rights == 0xFFFFFFFF) { return 0xFFFFFFFF; } // 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 0xFFFFFFFF; } // User has full rights thru a user group, stop here.
rights |= r.rights; // TODO: Deal with reverse permissions
}
}
}
}
return rights;
}
// 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 (userinfo.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 r;
var user = null;
// 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
/*
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 r;
}
// 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 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() { onDevicesScroll(); QS('dialog').left = ((((getDocWidth() - 300) / 2)) + 'px'); deskAdjust(); deskAdjust(); /*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;color:darkblue;text-decoration:none onclick=\'' + f + '\'>&diams; ' + 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('&nbsp;'); }
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>'; }
</script>
</body>
</html>