mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-12-27 15:45:53 -05:00
14194 lines
870 KiB
Handlebars
14194 lines
870 KiB
Handlebars
<!DOCTYPE html> <html dir="ltr" xmlns="http://www.w3.org/1999/xhtml"> <head> <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="apple-mobile-web-app-capable" content="yes"> <meta name="format-detection" content="telephone=no"> <link rel="shortcut icon" type="image/x-icon" href="{{{domainurl}}}favicon.ico"> <link keeplink="1" type="text/css" href="styles/style.css" media="screen" rel="stylesheet" title="CSS"> <style>.ol-box{box-sizing:border-box;border-radius:2px;border:2px solid #00f;}.ol-mouse-position{top:8px;right:8px;position:absolute;}.ol-scale-line{background:rgba(0,60,136,.3);border-radius:4px;bottom:8px;left:8px;padding:2px;position:absolute;}.ol-scale-line-inner{border:1px solid #eee;border-top:none;color:#eee;font-size:10px;text-align:center;margin:1px;will-change:contents,width;}.ol-overlay-container{will-change:left,right,top,bottom;}.ol-unsupported{display:none;}.ol-unselectable, .ol-viewport{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent;}.ol-selectable{-webkit-touch-callout:default;-webkit-user-select:auto;-moz-user-select:auto;-ms-user-select:auto;user-select:auto;}.ol-grabbing{cursor:-webkit-grabbing;cursor:-moz-grabbing;cursor:grabbing;}.ol-grab{cursor:move;cursor:-webkit-grab;cursor:-moz-grab;cursor:grab;}.ol-control{position:absolute;background-color:rgba(255,255,255,.4);border-radius:4px;padding:2px;}.ol-control:hover{background-color:rgba(255,255,255,.6);}.ol-zoom{top:.5em;right:.5em;}.ol-rotate{top:.5em;right:.5em;transition:opacity .25s linear,visibility 0s linear;}.ol-rotate.ol-hidden{opacity:0;visibility:hidden;transition:opacity .25s linear,visibility 0s linear .25s;}.ol-zoom-extent{top:4.643em;left:.5em;}.ol-full-screen{right:.5em;top:.5em;}@media print{.ol-control{display:none;}}.ol-control button{display:block;margin:1px;padding:0;color:#fff;font-size:1.14em;font-weight:700;text-decoration:none;text-align:center;height:1.375em;width:1.375em;line-height:.4em;background-color:rgba(0,60,136,.5);border:none;border-radius:2px;}.ol-control button::-moz-focus-inner{border:none;padding:0;}.ol-zoom-extent button{line-height:1.4em;}.ol-compass{display:block;font-weight:400;font-size:1.2em;will-change:transform;}.ol-touch .ol-control button{font-size:1.5em;}.ol-touch .ol-zoom-extent{top:5.5em;}.ol-control button:focus, .ol-control button:hover{text-decoration:none;background-color:rgba(0,60,136,.7);}.ol-zoom .ol-zoom-in{border-radius:2px 2px 0 0;}.ol-zoom .ol-zoom-out{border-radius:0 0 2px 2px;}.ol-attribution{text-align:right;bottom:.5em;right:.5em;max-width:calc(100% - 1.3em);}.ol-attribution ul{margin:0;padding:0 .5em;font-size:.7rem;line-height:1.375em;color:#000;text-shadow:0 0 2px #fff;}.ol-attribution li{display:inline;list-style:none;line-height:inherit;}.ol-attribution li:not(:last-child):after{content:" ";}.ol-attribution img{max-height:2em;max-width:inherit;vertical-align:middle;}.ol-attribution button, .ol-attribution ul{display:inline-block;}.ol-attribution.ol-collapsed ul{display:none;}.ol-attribution.ol-logo-only ul{display:block;}.ol-attribution:not(.ol-collapsed){background:rgba(255,255,255,.8);}.ol-attribution.ol-uncollapsible{bottom:0;right:0;border-radius:4px 0 0;height:1.1em;line-height:1em;}.ol-attribution.ol-logo-only{background:0 0;bottom:.4em;height:1.1em;line-height:1em;}.ol-attribution.ol-uncollapsible img{margin-top:-.2em;max-height:1.6em;}.ol-attribution.ol-logo-only button, .ol-attribution.ol-uncollapsible button{display:none;}.ol-zoomslider{top:4.5em;left:.5em;height:200px;}.ol-zoomslider button{position:relative;height:10px;}.ol-touch .ol-zoomslider{top:5.5em;}.ol-overviewmap{left:.5em;bottom:.5em;}.ol-overviewmap.ol-uncollapsible{bottom:0;left:0;border-radius:0 4px 0 0;}.ol-overviewmap .ol-overviewmap-map, .ol-overviewmap button{display:inline-block;}.ol-overviewmap .ol-overviewmap-map{border:1px solid #7b98bc;height:150px;margin:2px;width:150px;}.ol-overviewmap:not(.ol-collapsed) button{bottom:1px;left:2px;position:absolute;}.ol-overviewmap.ol-collapsed .ol-overviewmap-map, .ol-overviewmap.ol-uncollapsible button{display:none;}.ol-overviewmap:not(.ol-collapsed){background:rgba(255,255,255,.8);}.ol-overviewmap-box{border:2px dotted rgba(0,60,136,.7);}.ol-overviewmap .ol-overviewmap-box:hover{cursor:move;}</style> <style> .ol-ctx-menu-container{position:absolute;padding:8px;background:#fff;color:#222;font-size:13px;border-radius:5px;box-shadow:3px 3px 5px rgba(0,0,0,.2);box-sizing:border-box}.ol-ctx-menu-container a,.ol-ctx-menu-container div,.ol-ctx-menu-container img,.ol-ctx-menu-container li,.ol-ctx-menu-container span,.ol-ctx-menu-container ul{margin:0;padding:0;border:0;font:inherit;font-size:100%;vertical-align:baseline}.ol-ctx-menu-container a img{border:none}.ol-ctx-menu-container *,.ol-ctx-menu-container :after,.ol-ctx-menu-container :before{box-sizing:inherit}.ol-ctx-menu-container.ol-ctx-menu-hidden{opacity:0;visibility:hidden;-webkit-transition:visibility 0s linear .3s,opacity .3s;transition:visibility 0s linear .3s,opacity .3s}.ol-ctx-menu-container ul{list-style:none}.ol-ctx-menu-container li{position:relative;line-height:20px;padding:2px 5px}.ol-ctx-menu-container li:not(.ol-ctx-menu-separator):hover{cursor:pointer;background-color:#333;color:#eee}.ol-ctx-menu-container li.ol-ctx-menu-submenu .ol-ctx-menu-container{border:1px solid #eee;padding:8px;top:0;opacity:0;visibility:hidden;-webkit-transition:visibility 0s linear .3s,opacity .3s;transition:visibility 0s linear .3s,opacity .3s}.ol-ctx-menu-container li.ol-ctx-menu-submenu:hover .ol-ctx-menu-container{opacity:1;visibility:visible;-webkit-transition-delay:0s;transition-delay:0s}.ol-ctx-menu-container li.ol-ctx-menu-submenu:after{position:absolute;top:7px;right:10px;content:"";display:inline-block;width:.6em;height:.6em;border-right:.3em solid #222;border-top:.3em solid #222;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.ol-ctx-menu-container li.ol-ctx-menu-submenu:hover:after{border-color:#eee}.ol-ctx-menu-container li.ol-ctx-menu-separator{padding:0}.ol-ctx-menu-container li.ol-ctx-menu-separator hr{border:0;height:1px;background-image:-webkit-linear-gradient(right,transparent,rgba(0,0,0,.75),transparent);background-image:linear-gradient(270deg,transparent,rgba(0,0,0,.75),transparent)}.ol-ctx-menu-icon{text-indent:20px;background-size:20px auto;background-repeat:no-repeat;background-position:0}.ol-ctx-menu-zoom-in{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAABaUlEQVQ4T72U7VHCQBCGn90GtAMuNGCswFiBWIFQgWMFxg6wArECsQKhArEBiB1Qwa1zgQn5IAYcxv13k71n3919L8KJQ07M47+BzgG9TRfZ/JBuWhS6BJFHRJICYrZGZIz3z5Ct2+B7gG6I6kt+wewdkQVwjtkAkR5mC8yu26A1oItR/cTsOweQBdgutD8G7jGm2PJ2n8oqUKIpIjd4HxTM8gvaT/F+AlmWnyWaIXKF95eNguFzTYFhNsdWu9kFgFlaFMANUH3D8wDLoLgSTSD2il8NCe2ZXQBxWDGwxmyUzzOMBZ7wy7Qb2K0wQfXjMOBuhlFpZtNty5sFaTQBuTusZdymeqs1SpYKcO9HkE3KbTd9WFijMHJQ5hBNEAYNq5Qd0dhyke0GiE4QzjqfW23mHT8Hl4DG4Lce3FPE7AtbBSdsbNqpoJLgYkRnNeUV+xwJDHTnUEkxHGbhBXUs5TjJjew/KPy94g+NRaIVRYmMXwAAAABJRU5ErkJggg==")}.ol-ctx-menu-container li:hover.ol-ctx-menu-zoom-in{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAABc0lEQVQ4T71U21ECQRDsJgGdvQDECMQIxAjECMQILCPwzAAjECIQI0AiEDPQAPaWCBhrcKHuCUcV5f7dY3v6tUscefHIePhfwBBCF8CZqRCReRs1tQxDCH1VfQLQz4EsSY4AvIjIsgm8AhhCGKrqa9zwrqoLAKckB5HtguR1E2gBMITQU9VPAD8GICIGtl3e+xHJBwBT59xtHcsCYJZlUwA3kcGHbfDep51OZywi3/acZZm9vyJ5WR5o38uACmDunNt6ZwAkUxFZDwghDFT1jeSjiJinhVUBVNVJkiTDKO8CQA+AsbNQ7s1Ps0VVn5MkSfcCtmBoDZi1Bdx4eJ7zbBolrwPy3o9J3rWSHPs3A1BbjVKlYBaIyDgvu9LDXDU2RTZmXVW1oKyLxRD+OrkOrJLy5mVM0iaftDhuhVbsvBzMglzKUNW6IV/OOWtCM8MmVvEkmbwt83LaB19fdgOtVquUZJeknaDdobTwbOcvBzPcN/AXH1DFFWP7u9oAAAAASUVORK5CYII=")}.ol-ctx-menu-zoom-out{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAABU0lEQVQ4T72U7VECMRRFz3sNaAdkacC1AtcKxApcKnCsQOwAK3CtQKxAqEBsANYOqCDPyTIC+8WCw5jfybn33dxEOPGSE/P4b6BzQG89RT47ZJoWhy5B5BGRZAMxWyEyxvtnyFdt8AagS1F9KQ6YvSMyB84xGyDSw2yO2XUbtAJ0MaqfmH0XAPIA2y7tj4F7jAm2uG1yWQZKNEHkBu+Dg2njWBJNEbnC+8uaIFRuWfuG2QxbbrOrUd0A1Tc8D7AIjkur7DAAsVf8MiWMZ3ZR2m02LPIMscATfjHqBnY7TFD9OAy4zTCCPG/MUKMM5O6wkXFr9dZq7FQqqHk/hDzbFa73cFONTZFDdRyiCcKg5rrSiLaXkiI6RjjrfG6VzDs+B5eAxuDXeYpmNRGzL2wZ/wof+du4GNFpBVqqz5HA4MM5VEYYDrOs+1I6Q9u/4Q8O9wN/AGgWjBVqQjjgAAAAAElFTkSuQmCC")}.ol-ctx-menu-container li:hover.ol-ctx-menu-zoom-out{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAABYklEQVQ4T72U4VHCQBCF36tA91KAWIFYgViBWIFYgWMFYgdYgVCBWAFSgdiBFpAsFWSdxcDkQoBkhnF/ZjbfvX377ogjF4/Mw/8CVbUD4MynEJF5k2lqFapqz8yeAPRKkCXJEYAXEVnugm8BVXVgZq/FD+9mtgBwSrJfqF2QvN4FjYCq2jWzTwA/DhARh20qTdMRyQcA0xDCbZ3KCJhl2RTATaHgo+6HLMv8+xXJy+qB3l8FGoB5CKHsXcRV1b6ZvZF8FBH3NKotoJlNkiQZFONdlLtJ3rufbouZPSdJMjwIbKDQEzBrClx7eC4i33Uepmk6JnnXaOQifzMAtdGoRApugYiMI1uqKkrRWAfZo9MxM1+UZzFewl8mN4nYdVM83L7BkwbXLUrF3sfBLQDQBbDy08x8vOohXyEE71lVq9emuEk+3gZa3XYroCvwFyjP8yHJDsnxwaU08GxvS2uFhw78BbzWrxXgMbsHAAAAAElFTkSuQmCC")}</style> <script type="text/javascript" src="scripts/u2f-api.js"></script> <script type="text/javascript" src="scripts/charts.js"></script> <script type="text/javascript" src="scripts/filesaver.js"></script> <script type="text/javascript" src="scripts/ol.js"></script> <script type="text/javascript" src="scripts/ol3-contextmenu.js"></script> <title>{{{title}}}</title> </head> <body id="body" onload="if (typeof(startup) !== 'undefined') startup();" oncontextmenu="handleContextMenu(event)" style="display:none"> <div id="contextMenu" class="contextMenu noselect" style="display:none"> <div id="cxinfo" class="cmtext" onclick="cmaction(1,event)"><b>Information</b></div> <div id="cxdesktop" class="cmtext" onclick="cmaction(3,event)">Desktop</div> <div id="cxterminal" class="cmtext" onclick="cmaction(2,event)">Terminal</div> <div id="cxfiles" class="cmtext" onclick="cmaction(4,event)">Files</div> <div id="cxevents" class="cmtext" onclick="cmaction(5,event)">Events</div> <div id="cxconsole" class="cmtext" onclick="cmaction(6,event)">Console</div> <hr id="cxmgroupsplit"> <div id="cxmdesktop" class="cmtext" onclick="cmaction(7,event)" style="display:none">Multi-Desktop</div> </div> <div id="meshContextMenu" class="contextMenu,noselect" style="display:none;min-width:0px"> <div id="cxselectall" class="cmtext" onclick="cmmeshaction(1,event)">Select All</div> <div id="cxselectnone" class="cmtext" onclick="cmmeshaction(2,event)">Select None</div> <hr id="cxmgroupsplit2" style="display:none"> <div id="cxmmdesktop" class="cmtext" style="display:none" onclick="cmmeshaction(3,event)">Multi-Desktop</div> </div> <div id="container"> <div id="notifiyBox" class="notifiyBox" style="display:none"></div> <div id="masthead" class="noselect"> <div class="title">{{{title}}}</div> <div class="title2">{{{title2}}}</div> <div style="float:right"> <div id="notificationCount" onclick="clickNotificationIcon()" class="unselectable" style="display:none;" title="Click to view current notifications">0</div> </div> <p id="logoutControl">{{{logoutControl}}}<span id="idleTimeoutNotify" style="color:yellow"></span></p> </div> <div id="page_leftbar"> <div style="height:16px"></div> <div id="LeftMenuMyDevices" class="lbbutton lbbuttonsel" title="My Devices" onclick="go(1)"> <div class="lb2"></div> </div> <div id="LeftMenuMyAccount" class="lbbutton" title="My Account" onclick="go(2)"> <div class="lb1"></div> </div> <div id="LeftMenuMyEvents" class="lbbutton" title="My Events" onclick="go(3)"> <div class="lb3"></div> </div> <div id="LeftMenuMyFiles" class="lbbutton" style="display:none" title="My Files" onclick="go(5)"> <div class="lb4"></div> </div> <div id="LeftMenuMyUsers" class="lbbutton" style="display:none" title="My Users" onclick="go(4)"> <div class="lb5"></div> </div> <div id="LeftMenuMyServer" class="lbbutton" style="display:none" title="My Server" onclick="go(6)" style="display:none"> <div class="lb6"></div> </div> </div> <div id="topbar" class="noselect"> <div> <div style="position:relative"> <div id="uiMenuButton" title="User interface selection" onclick="showUserInterfaceSelectMenu()">♦ <div id="uiMenu" style="display:none"> <div id="uiViewButton1" class="uiSelector" onclick="userInterfaceSelectMenu(1)" title="Left bar interface"><div class="uiSelector1"></div></div> <div id="uiViewButton2" class="uiSelector" onclick="userInterfaceSelectMenu(2)" title="Top bar interface"><div class="uiSelector2"></div></div> <div id="uiViewButton3" class="uiSelector" onclick="userInterfaceSelectMenu(3)" title="Fixed width interface"><div class="uiSelector3"></div></div> <div id="uiViewButton4" class="uiSelector" onclick="toggleNightMode()" title="Toggle night mode"><div class="uiSelector4"></div></div> </div> </div> <table id="MainMenuSpan" cellpadding="0" cellspacing="0" class="style1"> <tr> <td id="MainMenuMyDevices" class="topbar_td style3x" onclick="go(1)">My Devices</td> <td id="MainMenuMyAccount" class="topbar_td style3x" onclick="go(2)">My Account</td> <td id="MainMenuMyEvents" class="topbar_td style3x" onclick="go(3)">My Events</td> <td id="MainMenuMyFiles" class="topbar_td style3x" onclick="go(5)">My Files</td> <td id="MainMenuMyUsers" class="topbar_td style3x" onclick="go(4)">My Users</td> <td id="MainMenuMyServer" class="topbar_td style3x" onclick="go(6)">My Server</td> <td class="topbar_td_end style3"> </td> </tr> </table> <div id="MainSubMenuSpan" style="display:none"> <table id="MainSubMenu" cellpadding="0" cellspacing="0" class="style1"> <tr> <td id="MainDev" class="topbar_td style3x" onclick="go(10)">General</td> <td id="MainDevDesktop" class="topbar_td style3x" onclick="go(11)">Desktop</td> <td id="MainDevTerminal" class="topbar_td style3x" onclick="go(12)">Terminal</td> <td id="MainDevFiles" class="topbar_td style3x" onclick="go(13)">Files</td> <td id="MainDevEvents" class="topbar_td style3x" onclick="go(16)">Events</td> <td id="MainDevAmt" class="topbar_td style3x" onclick="go(14)">Intel® AMT</td> <td id="MainDevConsole" class="topbar_td style3x" onclick="go(15)">Console</td> <td class="topbar_td_end style3"> </td> </tr> </table> </div> <div id="MeshSubMenuSpan" style="display:none"> <table id="MeshSubMenu" cellpadding="0" cellspacing="0" class="style1"> <tr> <td id="MeshGeneral" class="topbar_td style3x" onclick="go(20)">General</td> <td class="topbar_td_end style3"> </td> </tr> </table> </div> <div id="UserSubMenuSpan" style="display:none"> <table id="UserSubMenu" cellpadding="0" cellspacing="0" class="style1"> <tr> <td id="UserGeneral" class="topbar_td style3x" onclick="go(30)">General</td> <td id="UserEvents" class="topbar_td style3x" onclick="go(31)">Events</td> <td class="topbar_td_end style3"> </td> </tr> </table> </div> <div id="ServerSubMenuSpan" style="display:none"> <table id="ServerSubMenu" cellpadding="0" cellspacing="0" class="style1"> <tr> <td id="ServerGeneral" class="topbar_td style3x" onclick="go(6)">General</td> <td id="ServerStats" class="topbar_td style3x" onclick="go(40)">Stats</td> <td id="ServerConsole" class="topbar_td style3x" onclick="go(115)">Console</td> <td class="topbar_td_end style3"> </td> </tr> </table> </div> <div id="UserDummyMenuSpan"> <table id="UserDummyMenu" cellpadding="0" cellspacing="0" class="style1"> <tr><td class="style3" style=""> </td></tr> </table> </div> </div> </div> </div> <div id="column_l"> <div id="p0" style="display:none"> <div id="p0message"><span id="p0span">Server disconnected</span>, <href onclick="reload()" style="cursor:pointer"><u>click to reconnect</u></href>.</div> </div> <div id="p1" style="display:none"> <div style="display:none" id="devListToolbarViewIcons"> <div id="devViewButton1" class="viewSelector" onclick="onDeviceViewChange(1)" title="Columns"><div class="viewSelector2"></div></div> <div id="devViewButton2" class="viewSelector" onclick="onDeviceViewChange(2)" title="List"><div class="viewSelector1"></div></div> <div id="devViewButton3" class="viewSelector" onclick="onDeviceViewChange(3)" title="Desktops"><div class="viewSelector3"></div></div> <div id="devViewButton4" class="viewSelector" onclick="onDeviceViewChange(4)" title="Map"><div class="viewSelector4"></div></div> </div><div><h1>My Devices</h1></div> <table id="devListToolbarSpan" class="noselect"> <tr> <td class="h1"></td> <td id="devListToolbar" class="style14" style="display:none"> <input type="button" id="SelectAllButton" onclick="selectallButtonFunction();" value="Select All"> <input type="button" id="GroupActionButton" disabled="disabled" value="Group Action" onclick="groupActionFunction()"> <input id="SearchInput" type="text" placeholder="Filter" onchange="masterUpdate(5)" onkeyup="masterUpdate(5)" autocomplete="off" onfocus="onSearchFocus(1)" onblur="onSearchFocus(0)"> <label><input type="checkbox" id="RealNameCheckBox" onclick="onRealNameCheckBox()"><span title="Show devices operating system name">OS Name</span></label> </td> <td id="kvmListToolbar" class="style14" style="display:none"> <input type="button" onclick="connectAllKvmFunction()" value="Connect All"> <input type="button" onclick="disconnectAllKvmFunction()" value="Disconnect All"> <label><input type="checkbox" id="autoConnectDesktopCheckbox" onclick="autoConnectDesktops(event)" title="Automatic connect">Auto </label> <input type="button" onclick="showMultiDesktopSettings()" value="Settings"> </td> <td id="devMapToolbar" class="style14" style="display:none"> <input type="text" id="mapSearchLocation" placeholder="Search Location" onfocus="onMapSearchFocus(1)" onblur="onMapSearchFocus(0)"> <input type="button" value="Search" title="Search for location" onclick="getSearchLocation()"> <input type="button" id="refreshmap" title="Reset map view" value="Reset" onclick="refreshMap(false,true)"> </td> <td class="auto-style1" style="height:100%"> <div style="display:none" id="devListToolbarView"> View <select id="viewselect" onchange="onDeviceViewChange()"> <option value="1">Columns <option value="2">List <option value="3">Desktops <option id="viewselectmapoption" value="4">Map </select> </div> <div style="display:none" id="devListToolbarSort"> Sort <select id="sortselect" onchange="masterUpdate(6)"> <option>Group <option>Power <option>Device <option>Tags </select> </div> <div style="display:none" id="devListToolbarSize"> Size <select id="sizeselect" onchange="onDeviceViewChange()"> <option value="0">Small <option value="1">Medium <option value="2">Large </select> </div> </td> <td class="h2"></td> </tr> </table> <div id="NoMeshesPanel" style="display:none"> <table> <tr> <td valign="top" style="width:50px"> <img src="images/info.png"> </td> <td> <div id="getStarted1">To get started, <a onclick="account_createMesh()"><strong>click here to create a device group</strong></a>.</div> <div id="getStarted2">No device groups.</div> </td> </tr> </table> </div> <div id="xdevices" class="noselect" style="display:none"></div> <div id="xdevicesmap" style="display:none"> <div id="xmapSearchResultsDlg" style="display:none"> <div id="xmapSearchResultsBck"> <div id="xmapSearchClose" onclick="mapCloseSearchWindow()"><b>X</b></div> <div style="padding:5px">Location Results</div> <div style="width:100%;margin:6px"></div> </div> <div id="xmapSearchResults" style="margin:6px"></div> </div> </div> <div id="xmap-info-window"></div> </div> <div id="p2" style="display:none"> <h1>My Account</h1> <img id="p2AccountImage" alt="" src="images/clipboard-128.png"> <div id="p2AccountSecurity" style="display:none"> <p><strong>Account security</strong></p> <div style="margin-left:25px"> <div id="manageAuthApp"><div class="p2AccountActions"><span id="authAppSetupCheck"><strong>✓</strong></span></div><span><a onclick="account_manageAuthApp()">Manage authenticator app</a><br></span></div> <div id="manageHardwareOtp"><div class="p2AccountActions"><span id="authKeySetupCheck"><strong>✓</strong></span></div><span><a onclick="account_manageHardwareOtp(0)">Manage security keys</a><br></span></div> <div id="manageOtp"><div class="p2AccountActions"><span id="authCodesSetupCheck"><strong>✓</strong></span></div><span><a onclick="account_manageOtp(0)">Manage backup codes</a><br></span></div> </div> </div> <div id="p2AccountActions"> <p><strong>Account actions</strong></p> <p class="mL"> <span id="verifyEmailId" style="display:none"><a onclick="account_showVerifyEmail()">Verify email</a><br></span> <span id="accountEnableNotificationsSpan" style="display:none"><a onclick="account_enableNotifications()">Enable web notifications</a><br></span> <a onclick="account_showChangeEmail()">Change email address</a><br> <a onclick="account_showChangePassword()">Change password</a><span id="p2nextPasswordUpdateTime"></span><br> <a onclick="account_showDeleteAccount()">Delete account</a><br> </p> <br style="clear:both"> </div> <strong>Device Groups</strong> <span id="p2createMeshLink1">( <a onclick="account_createMesh()" class="newMeshBtn"> New</a> )</span> <br><br> <div id="p2meshes"></div> <div id="p2noMeshFound" style="display:none">No device groups.<span id="p2createMeshLink2"> <a onclick="account_createMesh()"><strong>Get started here!</strong></a></span></div> <br style="clear:both"> </div> <div id="p3" style="display:none"> <h1>My Events</h1> <table class="pTable"> <tr> <td class="h1"></td> <td> <input id="p2deleteall" type="button" onclick="showDeleteAllEventsDialog()" style="display:none" value="Delete All..."></td> <td class="auto-style1"> Show <select id="p3limitdropdown" onchange="refreshEvents()"> <option value="60">Last 60 <option value="120">Last 120 <option value="250">Last 250 <option value="500">Last 500 <option value="1000">Last 1000 </select> <img src="images/link4.png" height="10" width="10" title="Download Events" style="cursor:pointer" onclick="p3showDownloadEventsDialog()"> </td> <td class="h2"></td> </tr> </table> <div id="p3events" style=""></div> </div> <div id="p4" style="display:none"> <h1>My Users</h1> <table class="pTable"> <tr> <td class="h1"></td> <td class="style14"> <div style="float:right"> <input type="button" onclick="showUserBroadcastDialog()" style="margin-right:6px" value="Broadcast"> <img onclick="p4downloadUserInfo()" style="cursor:pointer" title="Download user information" src="images/link4.png"> <img id="p4UserBatchCreate" onclick="p4batchAccountCreate()" style="cursor:pointer;display:none" title="Batch create many user accounts" src="images/link6.png"> </div> <div> <input id="UserNewAccountButton" type="button" style="margin-left:6px" onclick="showCreateNewAccountDialog()" value="New Account..."> <input id="UserSearchInput" type="text" style="width:120px;margin-left:6px" placeholder="Filter" onchange="onUserSearchInputChanged()" onkeyup="onUserSearchInputChanged()" autocomplete="off" onfocus="onUserSearchFocus(1)" onblur="onUserSearchFocus(0)"> </div> </td> <td class="h2"></td> </tr> </table> <div id="p3users"></div> </div> <div id="p5" style="display:none"> <h1>My Files</h1> <table id="p5toolbar" cellpadding="0" cellspacing="0"> <tr> <td id="p5filehead" valign="bottom"> <div id="p5rightOfButtons"></div> <div> <input type="button" id="p5FolderUp" disabled="disabled" onclick="p5folderup();" value="Up"> <input type="button" id="p5SelectAllButton" disabled="disabled" onclick="p5selectallfile();" value="Select All" onkeypress="return false;" onkeydown="return false;"> <input type="button" id="p5RenameFileButton" disabled="disabled" value="Rename" onclick="p5renamefile();" onkeypress="return false;" onkeydown="return false;"> <input type="button" id="p5DeleteFileButton" disabled="disabled" value="Delete" onclick="p5deletefile();" onkeypress="return false;" onkeydown="return false;"> <input type="button" id="p5NewFolderButton" disabled="disabled" value="New Folder" onclick="p5createfolder();" onkeypress="return false;" onkeydown="return false;"> <input type="button" id="p5UploadButton" disabled="disabled" value="Upload" onclick="p5uploadFile()" onkeypress="return false;" onkeydown="return false;"> <input type="button" id="p5CutButton" disabled="disabled" value="Cut" onclick="p5copyFile(1)" onkeypress="return false" onkeydown="return false"> <input type="button" id="p5CopyButton" disabled="disabled" value="Copy" onclick="p5copyFile(0)" onkeypress="return false" onkeydown="return false"> <input type="button" id="p5PasteButton" disabled="disabled" value="Paste" onclick="p5pasteFile()" onkeypress="return false" onkeydown="return false"> </div> </td> </tr> <tr> <td id="p5filesubhead"> <div style="float:right"> <select id="p5sortdropdown" onchange="updateFiles()"> <option value="1" selected="selected">Sort by name <option value="2">Sort by size <option value="3">Sort by date <option value="4">Descend by name <option value="5">Descend by size <option value="6">Descend by date </select> </div> <div> <span id="p5currentpath"></span></div> </td> </tr> </table> <div id="p5filetable"> <div id="p5PublicShare" style=""><div>These files are shared publicly, click "link" to get public url.</div></div> <div id="bigok" style="display:none"><b>✓</b></div> <div id="bigfail" style="display:none"><b>✗</b></div> <span id="p5files"></span> </div> <table id="p5toolbarBottom" style="width:100%" cellpadding="0" cellspacing="0"> <tr><td class="style6"> <span id="p5bottomstatus"></span></td></tr> </table> </div> <div id="p6" style="display:none"> <img id="MainMeshImage" src="serverpic.ashx"> <h1>My Server</h1> <div id="p2ServerActions"> <p><strong>Server actions</strong></p> <div class="mL"> <div id="p2ServerActionsBackup"><a href="{{{domainurl}}}backup.zip" rel="noreferrer noopener" target="_blank">Download server backup</a></div> <div id="p2ServerActionsRestore"><a onclick="server_showRestoreDlg()">Restore server with backup</a></div> <div id="p2ServerActionsVersion"><a onclick="server_showVersionDlg()">Check server version</a></div> <div id="p2ServerActionsErrors"><a onclick="server_showErrorsDlg()">Show server error log</a></div> </div> </div> <br><strong>Server Statistics</strong><br><br> <div id="serverStats"> <div id="serverCpuChartView" style="display:none"> <div class="chartViewCanvas"><canvas id="serverCpuChart"></canvas></div> <div class="chartViewText" id="serverCpuChartText"></div> </div> <div id="serverMemoryChartView" style="display:none"> <div class="chartViewCanvas"><canvas id="serverMemoryChart"></canvas></div> <div class="chartViewText" id="serverMemoryChartText"></div> </div><br><br> <div id="serverStatsTable"></div> </div> </div> <div id="p10" style="display:none"> <table style="width:100%" cellpadding="0" cellspacing="0"> <tr> <td style="width:auto" valign="top"> <div id="p10title"> <div id="p10BackButton"><div class="backButton" onclick="goBack()" title="Back"><div class="backButtonEx"></div></div></div> <h1>General - <span id="p10deviceName"></span></h1> </div> <div id="p10html"></div> </td> <td style="width:20px"></td> <td style="width:200px"> <a onclick="p10showiconselector()"><img id="MainComputerImage"></a> <div id="MainComputerState"></div> </td> </tr> </table><br> <div id="p10html2"></div> <div id="p10html3"></div> </div> <div id="p11" class="noselect" style="display:none"> <div id="p11title"> <div id="p11deviceNameHeader"> <div id="p11BackButton"><div class="backButton" onclick="goBack()" title="Back"><div class="backButtonEx"></div></div></div> <div id="devListToolbarViewIcons"><div class="viewSelector" onclick="deskToggleFull(event)" title="Full Screen. Hold shift to browser full screen."><div class="viewSelector5"></div></div></div> <h1>Desktop - <span id="p11deviceName"></span></h1> </div> </div> <div id="p11warning" onclick="showFeaturesDlg()"> <div class="icon2"></div> <div class="warningbox">Intel® AMT Redirection port or KVM feature is disabled<span id="p11warninga">, click here to enable it.</span></div> </div> <div id="p11warning2" onclick="showPowerActionDlg()"> <div class="icon2"></div> <div class="warningbox">Remote computer is not powered on, click here to issue a power command.</div> </div> <div id="deskarea0" cellpadding="0" cellspacing="0"> <div id="deskarea1" class="areaHead"> <div class="toright2"> <span id="p11power"></span> <div class='deskareaicon' title="Toggle View Mode" onclick="toggleAspectRatio(1)">⇲</div> <div class='deskareaicon' title="Rotate Left" onclick="drotate(-1)">↺</div> <div class='deskareaicon' title="Rotate Right" onclick="drotate(1)">↻</div> <input id="deskFocusBtn" type="button" title="Toggle focus mode, when active only the region around the mouse is updated" onkeypress="return false" onkeydown="return false" value="Focus All" onclick="deskToggleFocus()" style="margin-right:3px;display:none"> <input id="deskSaveBtn" type="button" title="Save a screenshot of the remote desktop" onkeypress="return false" onkeydown="return false" value="Save..." onclick="deskSaveImage()" class="mR"> <input id="deskActionsBtn" type="button" title="Perform power actions on the device" onkeypress="return false" onkeydown="return false" value="Actions" onclick="deviceActionFunction()" class="mR"> <input id="deskActionsSettings" type="button" value="Settings..." title="Edit remote desktop settings" onkeypress="return false" onkeydown="return false" onclick="showDesktopSettings()" class="mR"> <input type="button" title="Change the power state of the remote machine" onkeypress="return false" onkeydown="return false" value="Power Actions..." onclick="showPowerActionDlg()" style="display:none"> </div> <div> <div id="idx_deskFullBtn2" onclick="deskToggleFull(event)"> ✖</div> <input type="button" id="autoconnectbutton1" value="AutoConnect" onclick="autoConnectDesktop(event)" onkeypress="return false" onkeydown="return false" style="display:none"> <span id="connectbutton1span"><input type="button" id="connectbutton1" value="Connect" onclick="connectDesktop(event,1)" onkeypress="return false" onkeydown="return false" disabled="disabled"></span> <span id="connectbutton1hspan"> <input type="button" id="connectbutton1h" value="HW Connect" onclick="connectDesktop(event,2)" onkeypress="return false" onkeydown="return false" disabled="disabled"></span> <span id="disconnectbutton1span"> <input type="button" id="disconnectbutton1" value="Disconnect" onclick="connectDesktop(event,0)" onkeypress="return false" onkeydown="return false"></span> <span id="deskstatus">Disconnected</span> </div> </div> <div id="deskarea2" style=""> <div class="areaProgress"><div id="progressbar" style=""></div></div> </div> <div id="deskarea3x"> <div id="DeskFocus" oncontextmenu="return false" onmousedown="dmousedown(event)" onmouseup="dmouseup(event)" onmousemove="dmousemove(event)"></div> <div id="DeskParent"> <canvas id="Desk" width="640" height="480" oncontextmenu="return false" onmousedown="dmousedown(event)" onmouseup="dmouseup(event)" onmousemove="dmousemove(event)" onmousewheel="dmousewheel(event)"></canvas> </div> <div id="DeskTools"> <a id="DeskToolsRefreshButton" style="" onclick="refreshDeskTools()">Refresh</a> <div id="DeskToolsBar">Processes</div> <div id="deskToolsArea"> <div id="deskToolsHeader"> <a class="colmn1" title="Sort by process id" onclick="sortProcess(0)">PID</a> <a class="colmn2" title="Sort by name" onclick="sortProcess(1)">Name</a></div> <div id="DeskToolsProcesses" style=""></div> </div> </div> <div id="p11DeskConsoleMsg" style="display:none;cursor:pointer;position:absolute;left:30px;top:17px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick="p11clearConsoleMsg()"></div> </div> <div id="deskarea4" class="areaFoot"> <div class="toright2"> <select id="termdisplays" style="display:none" onchange="deskSetDisplay(event)"></select> <input id="DeskToolsButton" type="button" value="Tools" title="Toggle tools view" onkeypress="return false" onkeydown="return false" onclick="toggleDeskTools()"> <span id="DeskChatButton" class="deskarea" title="Open chat window to this computer"><img src='images/icon-chat.png' onclick="deviceChat()" height="16" width="16" style="padding-top:2px"></span> <span id="DeskNotifyButton" title="Display a notification on the remote computer"><img src='images/icon-notify.png' onclick="deviceToastFunction()" height="16" width="16" style="padding-top:2px"></span> <span id="DeskOpenWebButton" title="Open a web address on remote computer"><img src='images/icon-url2.png' onclick="deviceUrlFunction()" height="16" width="16" style="padding-top:2px"></span> </div> <div> <select id="deskkeys"> <option value="5">Win <option value="0">Win+Down <option value="1">Win+Up <option value="2">Win+L <option value="3">Win+M <option value="4">Shift+Win+M <option value="6">Win+R <option value="7">Alt-F4 <option value="8">Ctrl-W <option value="9">Alt-Tab </select> <input id="DeskWD" type="button" value="Send" onkeypress="return false" onkeydown="return false" onclick="deskSendKeys()"> <input id="DeskClip" style="" type="button" value="Clipboard" onkeypress="return false" onkeydown="return false" onclick="showDeskClip()"> <input id="DeskCAD" type="button" value="Ctrl-Alt-Del" onkeypress="return false" onkeydown="return false" onclick="sendCAD()"> <label><span id="DeskControlSpan" title="Toggle mouse and keyboard input"><input id="DeskControl" type="checkbox" onkeypress="return false" onkeydown="return false" onclick="toggleKvmControl()">Input</span></label> </div> </div> </div> </div> <div id="p12" style="display:none"> <div id="p12title"> <div id="p12BackButton"><div class="backButton" onclick="goBack()" title="Back"><div class="backButtonEx"></div></div></div> <h1>Terminal - <span id="p12deviceName"></span></h1> </div> <div id="p12warning" onclick="showFeaturesDlg()"> <div class="icon2"></div> <div class="warningbox">Intel® AMT Redirection port or KVM feature is disabled<span id="p12warninga">, click here to enable it.</span></div> </div> <div id="p12warning2" onclick="showPowerActionDlg()"> <div class="icon2"></div> <div class="warningbox">Remote computer is not powered on, click here to issue a power command.</div> </div> <div id="termTable" style="position:relative"> <table style="width:100%" cellpadding="0" cellspacing="0"> <tr> <td class="areaHead"> <div class="toright2"> <input id="termActionsBtn" type="button" title="Perform power actions on the device" onkeypress="return false" onkeydown="return false" value="Actions" onclick="deviceActionFunction()/"> </div> <div> <input type="button" id="autoconnectbutton2" value="AutoConnect" onclick="autoConnectTerminal(event)" onkeypress="return false" onkeydown="return false" style="display:none"> <span id="connectbutton2span"><input type="button" id="connectbutton2" value="Connect" onclick="connectTerminal(event,1)" onkeypress="return false" onkeydown="return false" disabled="disabled"></span> <span id="connectbutton2hspan"> <input type="button" id="connectbutton2h" value="HW Connect" onclick="connectTerminal(event,2)" onkeypress="return false" onkeydown="return false" disabled="disabled"></span> <span id="disconnectbutton2span"> <input type="button" id="disconnectbutton2" value="Disconnect" onclick="connectTerminal(event,0)" onkeypress="return false" onkeydown="return false"></span> <span id="termstatus">Disconnected</span><span id="termtitle"></span> </div> </td> </tr> <tr> <td> <div class="areaProgress"><div id="termprogressbar" style=""></div></div> </td> </tr> <tr> <td id="termarea3x"> <pre id="Term"></pre> </td> </tr> <tr> <td class="areaFoot"> <div class="toright2"> <span id="terminalSettingsButtons" style="display:none"> <input id="id_tcrbutton" type="button" onkeypress="return false" onkeydown="return false" class="bottombutton" value="CR+LF" title="Toggle what the return key will send" onclick="termToggleCr()"> <input id="id_tfxkeysbutton" type="button" onkeypress="return false" onkeydown="return false" class="bottombutton" value="Intel (F10 = ESC+[OM)" title="Toggle F1 to F10 keys emulation type" onclick="termToggleFx()"> <input id="id_ttypebutton" type="button" onkeypress="return false" onkeydown="return false" class="bottombutton" value="Extended Ascii" title="Toggle terminal emulation type" onclick="termToggleType()"> </span> <span id="terminalSizeDropDown"> <select id="termSizeList" onkeypress="return false"><option value="1">80x25<option value="2">100x30<option value="3" selected="">Auto</select> </span> <select id="specialkeylist" onkeypress="return false"></select> <input id="specialkeylistinput" type="button" onkeypress="return false" class="bottombutton" value="Send" title="Send the selected special key" onclick="sendSpecialKey()"> </div> <div> <input type="button" onkeypress="return false" onkeydown="return false" class="bottombutton" id="ctrlcbutton" value="Ctl-C" onclick="termSendKey(3,'ctrlcbutton')"> <input type="button" onkeypress="return false" onkeydown="return false" class="bottombutton" id="ctrlxbutton" value="Ctl-X" onclick="termSendKey(24,'ctrlxbutton')"> <input type="button" onkeypress="return false" onkeydown="return false" class="bottombutton" id="escbutton" value="ESC" onclick="termSendKey(27,'escbutton')"> <input type="button" onkeypress="return false" onkeydown="return false" class="bottombutton" id="bsbutton" value="Backspace" onclick="termSendKey(8,'bsbutton')"> <input type="button" onkeypress="return false" onkeydown="return false" class="bottombutton" id="pastebutton" value="Paste" title="Paste text into the terminal" onclick="showTermPasteDialog()"> </div> </td> </tr> </table> <div id="p12TermConsoleMsg" style="display:none;cursor:pointer;position:absolute;left:30px;top:45px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick="p12clearConsoleMsg()"></div> </div> </div> <div id="p13" style="display:none"> <div id="p13title"> <div id="p13BackButton" style="float:left"><div class="backButton" onclick="goBack()" title="Back"><div class="backButtonEx"></div></div></div> <h1>Files - <span id="p13deviceName"></span></h1> </div> <table id="p13toolbar" cellpadding="0" cellspacing="0"> <tr> <td class="areaHead"> <div class="toright2"> <input id="filesActionsBtn" type="button" title="Perform power actions on the device" onkeypress="return false" onkeydown="return false" value="Actions" onclick="deviceActionFunction()"> </div> <div> <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 class="areaHead2" valign="bottom"> <div id="p13rightOfButtons" class="toright2"></div> <div> <input type="button" id="p13FolderUp" disabled="disabled" onclick="p13folderup()" value="Up"> <input type="button" id="p13SelectAllButton" disabled="disabled" onclick="p13selectallfile()" value="Select All" onkeypress="return false" onkeydown="return false"> <input type="button" id="p13RenameFileButton" disabled="disabled" value="Rename" onclick="p13renamefile()" onkeypress="return false" onkeydown="return false"> <input type="button" id="p13DeleteFileButton" disabled="disabled" value="Delete" onclick="p13deletefile()" onkeypress="return false" onkeydown="return false"> <input type="button" id="p13NewFolderButton" disabled="disabled" value="New Folder" onclick="p13createfolder()" onkeypress="return false" onkeydown="return false"> <input type="button" id="p13UploadButton" disabled="disabled" value="Upload" onclick="p13uploadFile()" onkeypress="return false" onkeydown="return false"> <input type="button" id="p13CutButton" disabled="disabled" value="Cut" onclick="p13copyFile(1)" onkeypress="return false" onkeydown="return false"> <input type="button" id="p13CopyButton" disabled="disabled" value="Copy" onclick="p13copyFile(0)" onkeypress="return false" onkeydown="return false"> <input type="button" id="p13PasteButton" disabled="disabled" value="Paste" onclick="p13pasteFile()" onkeypress="return false" onkeydown="return false"> <input type="button" id="p13RefreshButton" disabled="disabled" value="Refresh" onclick="p13folderup(9999)" onkeypress="return false" onkeydown="return false"> </div> </td> </tr> <tr> <td class="areaHead3"> <div class="toright2"> <select id="p13sortdropdown" onchange="p13updateFiles()"> <option value="1" selected="selected">Sort by name <option value="2">Sort by size <option value="3">Sort by date <option value="4">Descend by name <option value="5">Descend by size <option value="6">Descend by date </select> </div> <div> <span id="p13currentpath"></span></div> </td> </tr> </table> <div id="p13FilesConsoleMsg" style="display:none;cursor:pointer;position:absolute;left:30px;top:165px;color:yellow;background-color:rgba(0,0,0,0.6);padding:10px;border-radius:5px" onclick="p13clearConsoleMsg()"></div> <div id="p13filetable" style=""> <div id="p13bigok" style="display:none"><b>✓</b></div> <div id="p13bigfail" style="display:none"><b>✗</b></div> <span id="p13files"></span> </div> <table id="p13toolbarBottom" cellpadding="0" cellspacing="0"> <tr><td class="style6"> <span id="p13bottomstatus"></span></td></tr> </table> </div> <div id="p14" style="display:none"> <div id="p14title"> <div id="p14BackButton" style="float:left"><div class="backButton" onclick="goBack()" title="Back"><div class="backButtonEx"></div></div></div> <div id="devListToolbarViewIcons"><div class="viewSelector" onclick="deskToggleFull(event)" title="Full Screen. Hold shift to browser full screen."><div class="viewSelector5"></div></div></div> <h1>Intel® AMT - <span id="p14deviceName"></span></h1> </div> <iframe id="p14iframe" src="{{{domainurl}}}commander.htm"></iframe> </div> <div id="p15" style="display:none"> <div id="p15title"> <div id="p15BackButton" style="float:left"><div class="backButton" onclick="goBack()" title="Back"><div class="backButtonEx"></div></div></div> <h1><span id="p15deviceName"></span></h1> </div> <table id="consoleTable" cellpadding="0" cellspacing="0"> <tr> <td class="areaHead"> <div class="toright2"> <div id="p15coreName" title="Information about current core running on this agent"></div> <input type="button" id="p15uploadCore" value="Agent Action" onclick="p15uploadCore(event)" title="Change the agent Java Script code module"> <img onclick="p15downloadConsoleText()" style="cursor:pointer;margin-top:6px" title="Download console text" src="images/link4.png"> </div> <div id="p15statetext"></div> </td> </tr> <tr> <td> <div class="areaProgress"><div id="consoleprogressbar" style=""></div></div> </td> </tr> <tr> <td id="p15agentConsole"> <pre id="p15agentConsoleText"></pre> </td> </tr> <tr> <td class="areaFoot"> <table style="width:100%"> <tr> <td style="width:99%"> <input id="p15consoleText" style="width:100%" onkeyup="p15consoleSend(event)" onfocus="onConsoleFocus(1)" onblur="onConsoleFocus(0)"> </td> <td> </td> <td style="width:1%"><input id="id_p15consoleClear" type="button" onkeypress="return false" onkeydown="return false" class="bottombutton" value="Clear" onclick="p15consoleClear()"></td> </tr> </table> </td> </tr> </table> </div> <div id="p16" style="display:none"> <div id="p16title"> <div id="p16BackButton" style="float:left"><div class="backButton" onclick="goBack()" title="Back"><div class="backButtonEx"></div></div></div> <h1>Events - <span id="p16deviceName"></span></h1> </div> <table class="pTable"> <tr> <td class="h1"></td> <td> <input type="button" onclick="refreshDeviceEvents()" value="Refresh"></td> <td class="auto-style1"> Show <select id="p16limitdropdown" onchange="refreshDeviceEvents()"> <option value="60">Last 60 <option value="120">Last 120 <option value="250">Last 250 <option value="500">Last 500 <option value="1000">Last 1000 </select> </td> <td class="h2"></td> </tr> </table> <div id="p16events"></div> </div> <div id="p20" style="display:none"> <picture id="MainMeshImage" style="border-width:0px;height:200px;width:200px;float:right"> <source type="image/webp" width="200" height="200" srcset="images/webp/mesh-256.webp"> <img alt="" width="200" height="200" src="images/mesh-256.png"> </source></picture> <div style="float:left"><div class="backButton" onclick="goBack()" title="Back"><div class="backButtonEx"></div></div></div> <h1>General - <span id="p20meshName"></span></h1> <p id="p20info"> </div> <div id="p30" style="display:none"> <table style="width:100%" cellpadding="0" cellspacing="0"> <tr> <td style="width:auto" valign="top"> <div id="p30title"> <div style="float:left"><div class="backButton" onclick="goBack()" title="Back"><div class="backButtonEx"></div></div></div> <h1>General - <span id="p30userName"></span></h1> </div> <div id="p30html"></div> </td> <td style="width:20px"></td> <td style="width:200px"> <picture id="MainUserImage" style="border-width:0px;height:200px;width:200px;float:right"> <source type="image/webp" width="200" height="200" srcset="images/webp/user-256.webp"> <img alt="" width="200" height="200" src="images/user-256.png"> </source></picture> <div style="width:100%;text-align:center"><strong><span id="MainUserState"></span></strong></div> </td> </tr> </table><br> <div id="p30html2"></div> <div id="p30html3"></div> </div> <div id="p31" style="display:none"> <div style="float:left"><div class="backButton" onclick="goBack()" title="Back"><div class="backButtonEx"></div></div></div> <h1>Events - <span id="p31userName"></span></h1> <table class="pTable"> <tr> <td class="h1"></td> <td> <input type="button" onclick="refreshUsersEvents()" value="Refresh"></td> <td class="auto-style1"> Show <select id="p31limitdropdown" onchange="refreshUsersEvents()"> <option value="60">Last 60 <option value="120">Last 120 <option value="250">Last 250 <option value="500">Last 500 <option value="1000">Last 1000 </select> </td> <td class="h2"></td> </tr> </table> <div id="p31events"></div> </div> <div id="p40" style="display:none;"> <h1>My Server Stats</h1> <div class="areaHead"> <div class="toright2"> <select id="p40type" onchange="updateServerTimelineStats()"> <option value="0">Connections <option value="1">Memory </select> <select id="p40time" onchange="updateServerTimelineHours()"> <option value="3">Last 3 hours <option value="8">Last 8 hours <option value="24">Last day <option value="168">Last week <option value="720">Last 30 days </select> <img src="images/link4.png" height="10" width="10" title="Download data points (.csv)" style="cursor:pointer" onclick="p40downloadEvents()"> </div> <div> <input value="Refresh" type="button" onclick="refreshServerTimelineStats()"> <input id="p40log" type="checkbox" onclick="updateServerTimelineHours()">Log-X </div> </div> <canvas id="serverMainStats" style=""></canvas> </div> <br id="column_l_bottomgap"> </div> <div id="footer"> <div class="footer1">{{{footer}}}</div> <div class="footer2"> <a id="verifyEmailId2" style="display:none" onclick="account_showVerifyEmail()">Verify Email</a> <a href="terms">Terms & Privacy</a> </div> </div> <div id="dialog" style="display:none"> <div id="dialogHeader"> <div id="id_dialogclose" onclick="setDialogMode()">✖</div> <div id="id_dialogtitle"></div> </div> <div id="dialogBody"> <div id="dialog1"> <div id="id_dialogMessage" style=""></div> </div> <div id="dialog2" style=""> <div id="id_dialogOptions"></div> </div> <div id="dialog3" style=""> <div id="d3upload"> <div>File Selection</div> <select id="d3uploadMode" onchange="d3modechange()"> <option value="1">Local file upload <option value="2">Server file selection </select> </div> <div id="d3localmode" style="display:none"> <div>Upload File</div> <form id="d3localmodeform" method="post" enctype="multipart/form-data" action="uploadfile.ashx" target="fileUploadFrame"> <input type="text" id="d3attrib" name="attrib" style="display:none"> <input type="file" id="d3localFile" name="files" onchange="d3setActions()"> <input type="submit" id="d3submit" style="display:none"> </form> </div> <div id="d3servermode"> <div id="d3serveraction" valign="bottom"> <input type="button" id="p3FolderUp" disabled="disabled" onclick="d3folderup()" value="Up"> </div> <div id="d3serverfiles"></div> </div> </div> <div id="dialog7" style=""> <div id="d7meshkvm"> <h4>Agent Remote Desktop</h4> <div> <div>Quality</div> <select id="d7bitmapquality" dir="rtl"></select> </div> <div> <div>Scaling</div> <select id="d7bitmapscaling" style="" dir="rtl"> <option selected="selected" value="1024">100% <option value="896">87.5% <option value="768">75% <option value="640">62.5% <option value="512">50% <option value="384">37.5% <option value="256">25% <option value="128">12.5% </select> </div> <div> <div>Frame rate</div> <select id="d7framelimiter" dir="rtl"> <option selected="selected" value="50">Fast <option value="100">Medium <option value="400">Slow <option value="1000">Very slow </select> </div> </div> <div id="d7amtkvm"> <h4>Intel® AMT Hardware KVM</h4> <div> <div>Image Encoding</div> <select id="d7desktopmode"> <option value="1">RLE8, Fastest <option value="2">RLE16, Recommended <option value="3">RAW8, Slow <option value="4">RAW16, Very Slow </select> </div> <div> <div>Other Settings</div> <div id="d7otherset" style="display:block"> <label style="display:block"><input type="checkbox" id="d7showfocus">Show Focus Tool</label> <label style="display:block"><input type="checkbox" id="d7showcursor">Show Local Mouse Cursor</label> <label style="display:block"><input type="checkbox" id="d7localKeyMap">Local Keyboard Map</label> </div> </div> </div> </div> </div> <div id="idx_dlgButtonBar"> <input id="idx_dlgCancelButton" type="button" value="Cancel" style="" onclick="dialogclose(0)"> <input id="idx_dlgOkButton" type="button" value="OK" style="" onclick="dialogclose(1)"> <div><input id="idx_dlgDeleteButton" type="button" value="Delete" style="display:none" onclick="dialogclose(2)"></div> </div> </div> <iframe name="fileUploadFrame" style="display:none"></iframe> <form style="display:none" method="post" action="uploadfile.ashx" enctype="multipart/form-data" target="fileUploadFrame"><input id="p5fileDragName" name="name"><input id="p5fileDragSize" name="size"><input id="p5fileDragType" name="type"><input id="p5fileDragData" name="data"><input id="p5fileDragLink" name="link"><input type="submit" id="p5loginSubmit2" style="display:none"></form> <form style="display:none" method="post" action="uploadnodefile.ashx" enctype="multipart/form-data" target="fileUploadFrame"><input id="p13fileDragName" name="name"><input id="p13fileDragSize" name="size"><input id="p13fileDragType" name="type"><input id="p13fileDragData" name="data"><input id="p13fileDragLink" name="link"><input type="submit" id="p13loginSubmit2" style="display:none"></form> <audio id="chimes"><source src="sounds/chimes.mp3" type="audio/mp3"></source></audio> </div> <script>/**
|
||
* @description Set of short commonly used methods for handling HTML elements
|
||
* @author Ylian Saint-Hilaire
|
||
* @version v0.0.1b
|
||
*/
|
||
|
||
// Add startsWith for IE browser
|
||
if (!String.prototype.startsWith) { String.prototype.startsWith = function (str) { return this.lastIndexOf(str, 0) === 0; }; }
|
||
if (!String.prototype.endsWith) { String.prototype.endsWith = function (str) { return this.indexOf(str, this.length - str.length) !== -1; }; }
|
||
|
||
// Quick UI functions, a bit of a replacement for jQuery
|
||
//function Q(x) { if (document.getElementById(x) == null) { console.log('Invalid element: ' + x); } return document.getElementById(x); } // "Q"
|
||
function Q(x) { return document.getElementById(x); } // "Q"
|
||
function QS(x) { try { return Q(x).style; } catch (x) { } } // "Q" style
|
||
function QE(x, y) { try { Q(x).disabled = !y; } catch (x) { } } // "Q" enable
|
||
function QV(x, y) { try { QS(x).display = (y ? '' : 'none'); } catch (x) { } } // "Q" visible
|
||
function QA(x, y) { Q(x).innerHTML += y; } // "Q" append
|
||
function QH(x, y) { Q(x).innerHTML = y; } // "Q" html
|
||
function QC(x) { try { return Q(x).classList; } catch (x) { } } // "Q" class
|
||
|
||
// Move cursor to end of input box
|
||
function inputBoxFocus(x) { Q(x).focus(); var v = Q(x).value; Q(x).value = ''; Q(x).value = v; }
|
||
|
||
// Binary encoding and decoding functions
|
||
function ReadShort(v, p) { return (v.charCodeAt(p) << 8) + v.charCodeAt(p + 1); }
|
||
function ReadShortX(v, p) { return (v.charCodeAt(p + 1) << 8) + v.charCodeAt(p); }
|
||
function ReadInt(v, p) { return (v.charCodeAt(p) * 0x1000000) + (v.charCodeAt(p + 1) << 16) + (v.charCodeAt(p + 2) << 8) + v.charCodeAt(p + 3); } // We use "*0x1000000" instead of "<<24" because the shift converts the number to signed int32.
|
||
function ReadSInt(v, p) { return (v.charCodeAt(p) << 24) + (v.charCodeAt(p + 1) << 16) + (v.charCodeAt(p + 2) << 8) + v.charCodeAt(p + 3); }
|
||
function ReadIntX(v, p) { return (v.charCodeAt(p + 3) * 0x1000000) + (v.charCodeAt(p + 2) << 16) + (v.charCodeAt(p + 1) << 8) + v.charCodeAt(p); }
|
||
function ShortToStr(v) { return String.fromCharCode((v >> 8) & 0xFF, v & 0xFF); }
|
||
function ShortToStrX(v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF); }
|
||
function IntToStr(v) { return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); }
|
||
function IntToStrX(v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF, (v >> 24) & 0xFF); }
|
||
function MakeToArray(v) { if (!v || v == null || typeof v == 'object') return v; return [v]; }
|
||
function SplitArray(v) { return v.split(','); }
|
||
function Clone(v) { return JSON.parse(JSON.stringify(v)); }
|
||
function EscapeHtml(x) { if (typeof x == "string") return x.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<').replace(/"/g, '"').replace(/'/g, '''); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
|
||
function EscapeHtmlBreaks(x) { if (typeof x == "string") return x.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<').replace(/"/g, '"').replace(/'/g, ''').replace(/\r/g, '<br />').replace(/\n/g, '').replace(/\t/g, ' '); if (typeof x == "boolean") return x; if (typeof x == "number") return x; }
|
||
|
||
// Move an element from one position in an array to a new position
|
||
function ArrayElementMove(arr, from, to) { arr.splice(to, 0, arr.splice(from, 1)[0]); };
|
||
|
||
// Print object for HTML
|
||
function ObjectToStringEx(x, c) {
|
||
var r = "";
|
||
if (x != 0 && (!x || x == null)) return "(Null)";
|
||
if (x instanceof Array) { for (var i in x) { r += '<br />' + gap(c) + "Item #" + i + ": " + ObjectToStringEx(x[i], c + 1); } }
|
||
else if (x instanceof Object) { for (var i in x) { r += '<br />' + gap(c) + i + " = " + ObjectToStringEx(x[i], c + 1); } }
|
||
else { r += EscapeHtml(x); }
|
||
return r;
|
||
}
|
||
|
||
// Print object for console
|
||
function ObjectToStringEx2(x, c) {
|
||
var r = "";
|
||
if (x != 0 && (!x || x == null)) return "(Null)";
|
||
if (x instanceof Array) { for (var i in x) { r += '\r\n' + gap2(c) + "Item #" + i + ": " + ObjectToStringEx2(x[i], c + 1); } }
|
||
else if (x instanceof Object) { for (var i in x) { r += '\r\n' + gap2(c) + i + " = " + ObjectToStringEx2(x[i], c + 1); } }
|
||
else { r += EscapeHtml(x); }
|
||
return r;
|
||
}
|
||
|
||
// Create an ident gap
|
||
function gap(c) { var x = ''; for (var i = 0; i < (c * 4) ; i++) { x += ' '; } return x; }
|
||
function gap2(c) { var x = ''; for (var i = 0; i < (c * 4) ; i++) { x += ' '; } return x; }
|
||
|
||
// Print an object in html
|
||
function ObjectToString(x) { return ObjectToStringEx(x, 0); }
|
||
function ObjectToString2(x) { return ObjectToStringEx2(x, 0); }
|
||
|
||
// Convert a hex string to a raw string
|
||
function hex2rstr(d) {
|
||
if (typeof d != "string" || d.length == 0) return '';
|
||
var r = '', m = ('' + d).match(/../g), t;
|
||
while (t = m.shift()) r += String.fromCharCode('0x' + t);
|
||
return r
|
||
}
|
||
|
||
// Convert decimal to hex
|
||
function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }
|
||
|
||
// Convert a raw string to a hex string
|
||
function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }
|
||
|
||
// UTF-8 encoding & decoding functions
|
||
function encode_utf8(s) { return unescape(encodeURIComponent(s)); }
|
||
function decode_utf8(s) { return decodeURIComponent(escape(s)); }
|
||
|
||
// Convert a string into a blob
|
||
function data2blob(data) {
|
||
var bytes = new Array(data.length);
|
||
for (var i = 0; i < data.length; i++) bytes[i] = data.charCodeAt(i);
|
||
var blob = new Blob([new Uint8Array(bytes)]);
|
||
return blob;
|
||
}
|
||
|
||
// Generate random numbers
|
||
function random(max) { return Math.floor(Math.random() * max); }
|
||
|
||
// Trademarks
|
||
function trademarks(x) { return x.replace(/\(R\)/g, '®').replace(/\(TM\)/g, '™'); }
|
||
/**
|
||
* @fileoverview Meshcentral.js
|
||
* @author Ylian Saint-Hilaire
|
||
* @version v0.0.1
|
||
*/
|
||
|
||
var MeshServerCreateControl = function (domain, authCookie) {
|
||
var obj = {};
|
||
obj.State = 0;
|
||
obj.connectstate = 0;
|
||
obj.pingTimer = null;
|
||
obj.authCookie = authCookie;
|
||
obj.trace = false;
|
||
|
||
obj.xxStateChange = function (newstate, errCode) {
|
||
if (obj.State == newstate) return;
|
||
var previousState = obj.State;
|
||
obj.State = newstate;
|
||
if (obj.onStateChanged) obj.onStateChanged(obj, obj.State, previousState, errCode);
|
||
}
|
||
|
||
obj.Start = function () {
|
||
if (obj.connectstate != 0) return;
|
||
obj.connectstate = 0;
|
||
var url = window.location.protocol.replace("http", "ws") + "//" + window.location.host + domain + "control.ashx";
|
||
if (obj.authCookie && (obj.authCookie != '')) { url += '?auth=' + obj.authCookie; }
|
||
obj.socket = new WebSocket(url);
|
||
obj.socket.onopen = function (e) { obj.connectstate = 1; }
|
||
obj.socket.onmessage = obj.xxOnMessage;
|
||
obj.socket.onclose = function(e) { obj.Stop(e.code); }
|
||
obj.xxStateChange(1, 0);
|
||
if (obj.pingTimer != null) { clearInterval(obj.pingTimer); }
|
||
obj.pingTimer = setInterval(function () { obj.send({ action: 'ping' }); }, 29000); // Ping the server every 29 seconds, stops corporate proxies from disconnecting.
|
||
}
|
||
|
||
obj.Stop = function (errCode) {
|
||
obj.connectstate = 0;
|
||
if (obj.socket) { obj.socket.close(); delete obj.socket; }
|
||
if (obj.pingTimer != null) { clearInterval(obj.pingTimer); obj.pingTimer = null; }
|
||
obj.xxStateChange(0, errCode);
|
||
}
|
||
|
||
obj.xxOnMessage = function (e) {
|
||
if (obj.State == 1) { obj.xxStateChange(2); }
|
||
//console.log('xxOnMessage', e.data);
|
||
var message;
|
||
try { message = JSON.parse(e.data); } catch (e) { return; }
|
||
if ((typeof message != 'object') || (message.action == 'pong')) { return; }
|
||
if (message.action == 'close') { if (message.msg) { console.log(message.msg); } obj.Stop(message.cause); return; }
|
||
if (obj.trace) { console.log('RECV', message); }
|
||
if (obj.onMessage) obj.onMessage(obj, message);
|
||
};
|
||
|
||
obj.send = function (x) {
|
||
if (obj.socket != null && obj.connectstate == 1) {
|
||
if (obj.trace) { console.log('SEND', x); }
|
||
obj.socket.send(JSON.stringify(x));
|
||
}
|
||
}
|
||
|
||
return obj;
|
||
}
|
||
/**
|
||
* @fileoverview Intel(r) AMT Communication Stack
|
||
* @author Ylian Saint-Hilaire
|
||
* @version v0.2.0b
|
||
*/
|
||
|
||
/**
|
||
* Construct a AmtStackCreateService object, this ia the main Intel AMT communication stack.
|
||
* @constructor
|
||
*/
|
||
function AmtStackCreateService(wsmanStack) {
|
||
var obj = new Object();
|
||
obj.wsman = wsmanStack;
|
||
obj.pfx = ["http://intel.com/wbem/wscim/1/amt-schema/1/", "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/", "http://intel.com/wbem/wscim/1/ips-schema/1/"];
|
||
obj.PendingEnums = [];
|
||
obj.PendingBatchOperations = 0;
|
||
obj.ActiveEnumsCount = 0;
|
||
obj.MaxActiveEnumsCount = 1; // Maximum number of enumerations that can be done at the same time.
|
||
obj.onProcessChanged = null;
|
||
var _MaxProcess = 0;
|
||
var _LastProcess = 0;
|
||
|
||
// Return the number of pending actions
|
||
obj.GetPendingActions = function () { return (obj.PendingEnums.length * 2) + (obj.ActiveEnumsCount) + obj.wsman.comm.PendingAjax.length + obj.wsman.comm.ActiveAjaxCount + obj.PendingBatchOperations; }
|
||
|
||
// Private Method, Update the current processing status, this gives the application an idea of what progress is being done by the WSMAN stack
|
||
function _up() {
|
||
var x = obj.GetPendingActions();
|
||
if (_MaxProcess < x) _MaxProcess = x;
|
||
if (obj.onProcessChanged != null && _LastProcess != x) {
|
||
//console.log("Process Old=" + _LastProcess + ", New=" + x + ", PEnums=" + obj.PendingEnums.length + ", AEnums=" + obj.ActiveEnumsCount + ", PAjax=" + obj.wsman.comm.PendingAjax.length + ", AAjax=" + obj.wsman.comm.ActiveAjaxCount + ", PBatch=" + obj.PendingBatchOperations);
|
||
_LastProcess = x;
|
||
obj.onProcessChanged(x, _MaxProcess);
|
||
}
|
||
if (x == 0) _MaxProcess = 0;
|
||
}
|
||
|
||
// Perform a WSMAN "SUBSCRIBE" operation.
|
||
obj.Subscribe = function (name, delivery, url, callback, tag, pri, selectors, opaque, user, pass) { obj.wsman.ExecSubscribe(obj.CompleteName(name), delivery, url, function (ws, resuri, response, xstatus) { _up(); callback(obj, name, response, xstatus, tag); }, 0, pri, selectors, opaque, user, pass); _up(); }
|
||
|
||
// Perform a WSMAN "UNSUBSCRIBE" operation.
|
||
obj.UnSubscribe = function (name, callback, tag, pri, selectors) { obj.wsman.ExecUnSubscribe(obj.CompleteName(name), function (ws, resuri, response, xstatus) { _up(); callback(obj, name, response, xstatus, tag); }, 0, pri, selectors); _up(); }
|
||
|
||
// Perform a WSMAN "GET" operation.
|
||
obj.Get = function (name, callback, tag, pri) { obj.wsman.ExecGet(obj.CompleteName(name), function (ws, resuri, response, xstatus) { _up(); callback(obj, name, response, xstatus, tag); }, 0, pri); _up(); }
|
||
|
||
// Perform a WSMAN "PUT" operation.
|
||
obj.Put = function (name, putobj, callback, tag, pri, selectors) { obj.wsman.ExecPut(obj.CompleteName(name), putobj, function (ws, resuri, response, xstatus) { _up(); callback(obj, name, response, xstatus, tag); }, 0, pri, selectors); _up(); }
|
||
|
||
// Perform a WSMAN "CREATE" operation.
|
||
obj.Create = function (name, putobj, callback, tag, pri) { obj.wsman.ExecCreate(obj.CompleteName(name), putobj, function (ws, resuri, response, xstatus) { _up(); callback(obj, name, response, xstatus, tag); }, 0, pri); _up(); }
|
||
|
||
// Perform a WSMAN "DELETE" operation.
|
||
obj.Delete = function (name, putobj, callback, tag, pri) { obj.wsman.ExecDelete(obj.CompleteName(name), putobj, function (ws, resuri, response, xstatus) { _up(); callback(obj, name, response, xstatus, tag); }, 0, pri); _up(); }
|
||
|
||
// Perform a WSMAN method call operation.
|
||
obj.Exec = function (name, method, args, callback, tag, pri, selectors) { obj.wsman.ExecMethod(obj.CompleteName(name), method, args, function (ws, resuri, response, xstatus) { _up(); callback(obj, name, obj.CompleteExecResponse(response), xstatus, tag); }, 0, pri, selectors); _up(); }
|
||
|
||
// Perform a WSMAN method call operation.
|
||
obj.ExecWithXml = function (name, method, args, callback, tag, pri, selectors) { obj.wsman.ExecMethodXml(obj.CompleteName(name), method, execArgumentsToXml(args), function (ws, resuri, response, xstatus) { _up(); callback(obj, name, obj.CompleteExecResponse(response), xstatus, tag); }, 0, pri, selectors); _up(); }
|
||
|
||
// Perform a WSMAN "ENUMERATE" operation.
|
||
obj.Enum = function (name, callback, tag, pri) {
|
||
if (obj.ActiveEnumsCount < obj.MaxActiveEnumsCount) {
|
||
obj.ActiveEnumsCount++; obj.wsman.ExecEnum(obj.CompleteName(name), function (ws, resuri, response, xstatus, tag0) { _up(); _EnumStartSink(name, response, callback, resuri, xstatus, tag0); }, tag, pri);
|
||
} else {
|
||
obj.PendingEnums.push([name, callback, tag, pri]);
|
||
}
|
||
_up();
|
||
}
|
||
|
||
// Private method
|
||
function _EnumStartSink(name, response, callback, resuri, status, tag, pri) {
|
||
if (status != 200) { callback(obj, name, null, status, tag); _EnumDoNext(1); return; }
|
||
if (response == null || response.Header["Method"] != "EnumerateResponse" || !response.Body["EnumerationContext"]) { callback(obj, name, null, 603, tag); _EnumDoNext(1); return; }
|
||
var enumctx = response.Body["EnumerationContext"];
|
||
obj.wsman.ExecPull(resuri, enumctx, function (ws, resuri, response, xstatus) { _EnumContinueSink(name, response, callback, resuri, [], xstatus, tag, pri); });
|
||
}
|
||
|
||
// Private method
|
||
function _EnumContinueSink(name, response, callback, resuri, items, status, tag, pri) {
|
||
if (status != 200) { callback(obj, name, null, status, tag); _EnumDoNext(1); return; }
|
||
if (response == null || response.Header["Method"] != "PullResponse") { callback(obj, name, null, 604, tag); _EnumDoNext(1); return; }
|
||
for (var i in response.Body["Items"]) {
|
||
if (response.Body["Items"][i] instanceof Array) {
|
||
for (var j in response.Body["Items"][i]) { items.push(response.Body["Items"][i][j]); }
|
||
} else {
|
||
items.push(response.Body["Items"][i]);
|
||
}
|
||
}
|
||
if (response.Body["EnumerationContext"]) {
|
||
var enumctx = response.Body["EnumerationContext"];
|
||
obj.wsman.ExecPull(resuri, enumctx, function (ws, resuri, response, xstatus) { _EnumContinueSink(name, response, callback, resuri, items, xstatus, tag, 1); });
|
||
} else {
|
||
_EnumDoNext(1);
|
||
callback(obj, name, items, status, tag);
|
||
_up();
|
||
}
|
||
}
|
||
|
||
// Private method
|
||
function _EnumDoNext(dec) {
|
||
obj.ActiveEnumsCount -= dec;
|
||
if (obj.ActiveEnumsCount >= obj.MaxActiveEnumsCount || obj.PendingEnums.length == 0) return;
|
||
var x = obj.PendingEnums.shift();
|
||
obj.Enum(x[0], x[1], x[2]);
|
||
_EnumDoNext(0);
|
||
}
|
||
|
||
// Perform a batch of WSMAN "ENUM" operations.
|
||
obj.BatchEnum = function (batchname, names, callback, tag, continueOnError, pri) {
|
||
obj.PendingBatchOperations += (names.length * 2);
|
||
_BatchNextEnum(batchname, Clone(names), callback, tag, {}, continueOnError, pri); _up();
|
||
}
|
||
|
||
// Request each enum in the batch, stopping if something does not return status 200
|
||
function _BatchNextEnum(batchname, names, callback, tag, results, continueOnError, pri) {
|
||
obj.PendingBatchOperations -= 2;
|
||
var n = names.shift(), f = obj.Enum;
|
||
if (n[0] == '*') { f = obj.Get; n = n.substring(1); } // If the name starts with a star, do a GET instead of an ENUM. This will reduce round trips.
|
||
//console.log((f == obj.Get?'Get ':'Enum ') + n);
|
||
// Perform a GET/ENUM action
|
||
f(n, function (stack, name, responses, status, tag0) {
|
||
tag0[2][name] = { response: (responses==null?null:responses.Body), responses: responses, status: status };
|
||
if (tag0[1].length == 0 || status == 401 || (continueOnError != true && status != 200 && status != 400)) { obj.PendingBatchOperations -= (names.length * 2); _up(); callback(obj, batchname, tag0[2], status, tag); }
|
||
else { _up(); _BatchNextEnum(batchname, names, callback, tag, tag0[2], pri); }
|
||
}, [batchname, names, results], pri);
|
||
_up();
|
||
}
|
||
|
||
// Perform a batch of WSMAN "GET" operations.
|
||
obj.BatchGet = function (batchname, names, callback, tag, pri) {
|
||
_FetchNext({ name: batchname, names: names, callback: callback, current: 0, responses: {}, tag: tag, pri: pri }); _up();
|
||
}
|
||
|
||
// Private method
|
||
function _FetchNext(batch) {
|
||
if (batch.names.length <= batch.current) {
|
||
batch.callback(obj, batch.name, batch.responses, 200, batch.tag);
|
||
} else {
|
||
obj.wsman.ExecGet(obj.CompleteName(batch.names[batch.current]), function (ws, resuri, response, xstatus) { _Fetched(batch, response, xstatus); }, batch.pri);
|
||
batch.current++;
|
||
}
|
||
_up();
|
||
}
|
||
|
||
// Private method
|
||
function _Fetched(batch, response, status) {
|
||
if (response == null || status != 200) {
|
||
batch.callback(obj, batch.name, null, status, batch.tag);
|
||
} else {
|
||
batch.responses[response.Header["Method"]] = response;
|
||
_FetchNext(batch);
|
||
}
|
||
}
|
||
|
||
// Private method
|
||
obj.CompleteName = function(name) {
|
||
if (name.indexOf("AMT_") == 0) return obj.pfx[0] + name;
|
||
if (name.indexOf("CIM_") == 0) return obj.pfx[1] + name;
|
||
if (name.indexOf("IPS_") == 0) return obj.pfx[2] + name;
|
||
}
|
||
|
||
obj.CompleteExecResponse = function (resp) {
|
||
if (resp && resp != null && resp.Body && resp.Body["ReturnValue"]) resp.Body.ReturnValueStr = obj.AmtStatusToStr(resp.Body["ReturnValue"]);
|
||
return resp;
|
||
}
|
||
|
||
obj.RequestPowerStateChange = function (PowerState, callback_func) {
|
||
obj.CIM_PowerManagementService_RequestPowerStateChange(PowerState, "<Address xmlns=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">http://schemas.xmlsoap.org/ws/2004/08/addressing</Address><ReferenceParameters xmlns=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\"><ResourceURI xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ComputerSystem</ResourceURI><SelectorSet xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\"><Selector Name=\"CreationClassName\">CIM_ComputerSystem</Selector><Selector Name=\"Name\">ManagedSystem</Selector></SelectorSet></ReferenceParameters>", null, null, callback_func);
|
||
}
|
||
|
||
obj.SetBootConfigRole = function (Role, callback_func) {
|
||
obj.CIM_BootService_SetBootConfigRole("<Address xmlns=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\">http://schemas.xmlsoap.org/ws/2004/08/addressing</Address><ReferenceParameters xmlns=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\"><ResourceURI xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_BootConfigSetting</ResourceURI><SelectorSet xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\"><Selector Name=\"InstanceID\">Intel(r) AMT: Boot Configuration 0</Selector></SelectorSet></ReferenceParameters>", Role, callback_func);
|
||
}
|
||
|
||
// Cancel all pending queries with given status
|
||
obj.CancelAllQueries = function (s) {
|
||
obj.wsman.CancelAllQueries(s);
|
||
}
|
||
|
||
// Auto generated methods
|
||
obj.AMT_AgentPresenceWatchdog_RegisterAgent = function (callback_func) { obj.Exec("AMT_AgentPresenceWatchdog", "RegisterAgent", {}, callback_func); }
|
||
obj.AMT_AgentPresenceWatchdog_AssertPresence = function (SequenceNumber, callback_func) { obj.Exec("AMT_AgentPresenceWatchdog", "AssertPresence", { "SequenceNumber": SequenceNumber }, callback_func); }
|
||
obj.AMT_AgentPresenceWatchdog_AssertShutdown = function (SequenceNumber, callback_func) { obj.Exec("AMT_AgentPresenceWatchdog", "AssertShutdown", { "SequenceNumber": SequenceNumber }, callback_func); }
|
||
obj.AMT_AgentPresenceWatchdog_AddAction = function (OldState, NewState, EventOnTransition, ActionSd, ActionEac, callback_func, tag, pri, selectors) { obj.Exec("AMT_AgentPresenceWatchdog", "AddAction", { "OldState": OldState, "NewState": NewState, "EventOnTransition": EventOnTransition, "ActionSd": ActionSd, "ActionEac": ActionEac }, callback_func, tag, pri, selectors); }
|
||
obj.AMT_AgentPresenceWatchdog_DeleteAllActions = function (callback_func, tag, pri, selectors) { obj.Exec("AMT_AgentPresenceWatchdog", "DeleteAllActions", {}, callback_func, tag, pri, selectors); }
|
||
obj.AMT_AgentPresenceWatchdogAction_GetActionEac = function (callback_func) { obj.Exec("AMT_AgentPresenceWatchdogAction", "GetActionEac", {}, callback_func); }
|
||
obj.AMT_AgentPresenceWatchdogVA_RegisterAgent = function (callback_func) { obj.Exec("AMT_AgentPresenceWatchdogVA", "RegisterAgent", {}, callback_func); }
|
||
obj.AMT_AgentPresenceWatchdogVA_AssertPresence = function (SequenceNumber, callback_func) { obj.Exec("AMT_AgentPresenceWatchdogVA", "AssertPresence", { "SequenceNumber": SequenceNumber }, callback_func); }
|
||
obj.AMT_AgentPresenceWatchdogVA_AssertShutdown = function (SequenceNumber, callback_func) { obj.Exec("AMT_AgentPresenceWatchdogVA", "AssertShutdown", { "SequenceNumber": SequenceNumber }, callback_func); }
|
||
obj.AMT_AgentPresenceWatchdogVA_AddAction = function (OldState, NewState, EventOnTransition, ActionSd, ActionEac, callback_func) { obj.Exec("AMT_AgentPresenceWatchdogVA", "AddAction", { "OldState": OldState, "NewState": NewState, "EventOnTransition": EventOnTransition, "ActionSd": ActionSd, "ActionEac": ActionEac }, callback_func); }
|
||
obj.AMT_AgentPresenceWatchdogVA_DeleteAllActions = function (_method_dummy, callback_func) { obj.Exec("AMT_AgentPresenceWatchdogVA", "DeleteAllActions", { "_method_dummy": _method_dummy }, callback_func); }
|
||
obj.AMT_AuditLog_ClearLog = function (callback_func) { obj.Exec("AMT_AuditLog", "ClearLog", {}, callback_func); }
|
||
obj.AMT_AuditLog_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("AMT_AuditLog", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||
obj.AMT_AuditLog_ReadRecords = function (StartIndex, callback_func, tag) { obj.Exec("AMT_AuditLog", "ReadRecords", { "StartIndex": StartIndex }, callback_func, tag); }
|
||
obj.AMT_AuditLog_SetAuditLock = function (LockTimeoutInSeconds, Flag, Handle, callback_func) { obj.Exec("AMT_AuditLog", "SetAuditLock", { "LockTimeoutInSeconds": LockTimeoutInSeconds, "Flag": Flag, "Handle": Handle }, callback_func); }
|
||
obj.AMT_AuditLog_ExportAuditLogSignature = function (SigningMechanism, callback_func) { obj.Exec("AMT_AuditLog", "ExportAuditLogSignature", { "SigningMechanism": SigningMechanism }, callback_func); }
|
||
obj.AMT_AuditLog_SetSigningKeyMaterial = function (SigningMechanismType, SigningKey, LengthOfCertificates, Certificates, callback_func) { obj.Exec("AMT_AuditLog", "SetSigningKeyMaterial", { "SigningMechanismType": SigningMechanismType, "SigningKey": SigningKey, "LengthOfCertificates": LengthOfCertificates, "Certificates": Certificates }, callback_func); }
|
||
obj.AMT_AuditPolicyRule_SetAuditPolicy = function (Enable, AuditedAppID, EventID, PolicyType, callback_func) { obj.Exec("AMT_AuditPolicyRule", "SetAuditPolicy", { "Enable": Enable, "AuditedAppID": AuditedAppID, "EventID": EventID, "PolicyType": PolicyType }, callback_func); }
|
||
obj.AMT_AuditPolicyRule_SetAuditPolicyBulk = function (Enable, AuditedAppID, EventID, PolicyType, callback_func) { obj.Exec("AMT_AuditPolicyRule", "SetAuditPolicyBulk", { "Enable": Enable, "AuditedAppID": AuditedAppID, "EventID": EventID, "PolicyType": PolicyType }, callback_func); }
|
||
obj.AMT_AuthorizationService_AddUserAclEntryEx = function (DigestUsername, DigestPassword, KerberosUserSid, AccessPermission, Realms, callback_func) { obj.Exec("AMT_AuthorizationService", "AddUserAclEntryEx", { "DigestUsername": DigestUsername, "DigestPassword": DigestPassword, "KerberosUserSid": KerberosUserSid, "AccessPermission": AccessPermission, "Realms": Realms }, callback_func); }
|
||
obj.AMT_AuthorizationService_EnumerateUserAclEntries = function (StartIndex, callback_func) { obj.Exec("AMT_AuthorizationService", "EnumerateUserAclEntries", { "StartIndex": StartIndex }, callback_func); }
|
||
obj.AMT_AuthorizationService_GetUserAclEntryEx = function (Handle, callback_func, tag) { obj.Exec("AMT_AuthorizationService", "GetUserAclEntryEx", { "Handle": Handle }, callback_func, tag); }
|
||
obj.AMT_AuthorizationService_UpdateUserAclEntryEx = function (Handle, DigestUsername, DigestPassword, KerberosUserSid, AccessPermission, Realms, callback_func) { obj.Exec("AMT_AuthorizationService", "UpdateUserAclEntryEx", { "Handle": Handle, "DigestUsername": DigestUsername, "DigestPassword": DigestPassword, "KerberosUserSid": KerberosUserSid, "AccessPermission": AccessPermission, "Realms": Realms }, callback_func); }
|
||
obj.AMT_AuthorizationService_RemoveUserAclEntry = function (Handle, callback_func) { obj.Exec("AMT_AuthorizationService", "RemoveUserAclEntry", { "Handle": Handle }, callback_func); }
|
||
obj.AMT_AuthorizationService_SetAdminAclEntryEx = function (Username, DigestPassword, callback_func) { obj.Exec("AMT_AuthorizationService", "SetAdminAclEntryEx", { "Username": Username, "DigestPassword": DigestPassword }, callback_func); }
|
||
obj.AMT_AuthorizationService_GetAdminAclEntry = function (callback_func) { obj.Exec("AMT_AuthorizationService", "GetAdminAclEntry", {}, callback_func); }
|
||
obj.AMT_AuthorizationService_GetAdminAclEntryStatus = function (callback_func) { obj.Exec("AMT_AuthorizationService", "GetAdminAclEntryStatus", {}, callback_func); }
|
||
obj.AMT_AuthorizationService_GetAdminNetAclEntryStatus = function (callback_func) { obj.Exec("AMT_AuthorizationService", "GetAdminNetAclEntryStatus", {}, callback_func); }
|
||
obj.AMT_AuthorizationService_SetAclEnabledState = function (Handle, Enabled, callback_func, tag) { obj.Exec("AMT_AuthorizationService", "SetAclEnabledState", { "Handle": Handle, "Enabled": Enabled }, callback_func, tag); }
|
||
obj.AMT_AuthorizationService_GetAclEnabledState = function (Handle, callback_func, tag) { obj.Exec("AMT_AuthorizationService", "GetAclEnabledState", { "Handle": Handle }, callback_func, tag); }
|
||
obj.AMT_EndpointAccessControlService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("AMT_EndpointAccessControlService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||
obj.AMT_EndpointAccessControlService_GetPosture = function (PostureType, callback_func) { obj.Exec("AMT_EndpointAccessControlService", "GetPosture", { "PostureType": PostureType }, callback_func); }
|
||
obj.AMT_EndpointAccessControlService_GetPostureHash = function (PostureType, callback_func) { obj.Exec("AMT_EndpointAccessControlService", "GetPostureHash", { "PostureType": PostureType }, callback_func); }
|
||
obj.AMT_EndpointAccessControlService_UpdatePostureState = function (UpdateType, callback_func) { obj.Exec("AMT_EndpointAccessControlService", "UpdatePostureState", { "UpdateType": UpdateType }, callback_func); }
|
||
obj.AMT_EndpointAccessControlService_GetEacOptions = function (callback_func) { obj.Exec("AMT_EndpointAccessControlService", "GetEacOptions", {}, callback_func); }
|
||
obj.AMT_EndpointAccessControlService_SetEacOptions = function (EacVendors, PostureHashAlgorithm, callback_func) { obj.Exec("AMT_EndpointAccessControlService", "SetEacOptions", { "EacVendors": EacVendors, "PostureHashAlgorithm": PostureHashAlgorithm }, callback_func); }
|
||
obj.AMT_EnvironmentDetectionSettingData_SetSystemDefensePolicy = function (Policy, callback_func) { obj.Exec("AMT_EnvironmentDetectionSettingData", "SetSystemDefensePolicy", { "Policy": Policy }, callback_func); }
|
||
obj.AMT_EnvironmentDetectionSettingData_EnableVpnRouting = function (Enable, callback_func) { obj.Exec("AMT_EnvironmentDetectionSettingData", "EnableVpnRouting", { "Enable": Enable }, callback_func); }
|
||
obj.AMT_EthernetPortSettings_SetLinkPreference = function (LinkPreference, Timeout, callback_func) { obj.Exec("AMT_EthernetPortSettings", "SetLinkPreference", { "LinkPreference": LinkPreference, "Timeout": Timeout }, callback_func); }
|
||
obj.AMT_HeuristicPacketFilterStatistics_ResetSelectedStats = function (SelectedStatistics, callback_func) { obj.Exec("AMT_HeuristicPacketFilterStatistics", "ResetSelectedStats", { "SelectedStatistics": SelectedStatistics }, callback_func); }
|
||
obj.AMT_KerberosSettingData_GetCredentialCacheState = function (callback_func) { obj.Exec("AMT_KerberosSettingData", "GetCredentialCacheState", {}, callback_func); }
|
||
obj.AMT_KerberosSettingData_SetCredentialCacheState = function (Enable, callback_func) { obj.Exec("AMT_KerberosSettingData", "SetCredentialCacheState", { "Enable": Enable }, callback_func); }
|
||
obj.AMT_MessageLog_CancelIteration = function (IterationIdentifier, callback_func) { obj.Exec("AMT_MessageLog", "CancelIteration", { "IterationIdentifier": IterationIdentifier }, callback_func); }
|
||
obj.AMT_MessageLog_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("AMT_MessageLog", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||
obj.AMT_MessageLog_ClearLog = function (callback_func) { obj.Exec("AMT_MessageLog", "ClearLog", { }, callback_func); }
|
||
obj.AMT_MessageLog_GetRecords = function (IterationIdentifier, MaxReadRecords, callback_func, tag) { obj.Exec("AMT_MessageLog", "GetRecords", { "IterationIdentifier": IterationIdentifier, "MaxReadRecords": MaxReadRecords }, callback_func, tag); }
|
||
obj.AMT_MessageLog_GetRecord = function (IterationIdentifier, PositionToNext, callback_func) { obj.Exec("AMT_MessageLog", "GetRecord", { "IterationIdentifier": IterationIdentifier, "PositionToNext": PositionToNext }, callback_func); }
|
||
obj.AMT_MessageLog_PositionAtRecord = function (IterationIdentifier, MoveAbsolute, RecordNumber, callback_func) { obj.Exec("AMT_MessageLog", "PositionAtRecord", { "IterationIdentifier": IterationIdentifier, "MoveAbsolute": MoveAbsolute, "RecordNumber": RecordNumber }, callback_func); }
|
||
obj.AMT_MessageLog_PositionToFirstRecord = function (callback_func, tag) { obj.Exec("AMT_MessageLog", "PositionToFirstRecord", {}, callback_func, tag); }
|
||
obj.AMT_MessageLog_FreezeLog = function (Freeze, callback_func) { obj.Exec("AMT_MessageLog", "FreezeLog", { "Freeze": Freeze }, callback_func); }
|
||
obj.AMT_PublicKeyManagementService_AddCRL = function (Url, SerialNumbers, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "AddCRL", { "Url": Url, "SerialNumbers": SerialNumbers }, callback_func); }
|
||
obj.AMT_PublicKeyManagementService_ResetCRLList = function (_method_dummy, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "ResetCRLList", { "_method_dummy": _method_dummy }, callback_func); }
|
||
obj.AMT_PublicKeyManagementService_AddCertificate = function (CertificateBlob, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "AddCertificate", { "CertificateBlob": CertificateBlob }, callback_func); }
|
||
obj.AMT_PublicKeyManagementService_AddTrustedRootCertificate = function (CertificateBlob, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "AddTrustedRootCertificate", { "CertificateBlob": CertificateBlob }, callback_func); }
|
||
obj.AMT_PublicKeyManagementService_AddKey = function (KeyBlob, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "AddKey", { "KeyBlob": KeyBlob }, callback_func); }
|
||
obj.AMT_PublicKeyManagementService_GeneratePKCS10Request = function (KeyPair, DNName, Usage, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "GeneratePKCS10Request", { "KeyPair": KeyPair, "DNName": DNName, "Usage": Usage }, callback_func); }
|
||
obj.AMT_PublicKeyManagementService_GeneratePKCS10RequestEx = function (KeyPair, SigningAlgorithm, NullSignedCertificateRequest, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "GeneratePKCS10RequestEx", { "KeyPair": KeyPair, "SigningAlgorithm": SigningAlgorithm, "NullSignedCertificateRequest": NullSignedCertificateRequest }, callback_func); }
|
||
obj.AMT_PublicKeyManagementService_GenerateKeyPair = function (KeyAlgorithm, KeyLength, callback_func) { obj.Exec("AMT_PublicKeyManagementService", "GenerateKeyPair", { "KeyAlgorithm": KeyAlgorithm, "KeyLength": KeyLength }, callback_func); }
|
||
obj.AMT_RedirectionService_RequestStateChange = function (RequestedState, callback_func) { obj.Exec("AMT_RedirectionService", "RequestStateChange", { "RequestedState": RequestedState }, callback_func); }
|
||
obj.AMT_RedirectionService_TerminateSession = function (SessionType, callback_func) { obj.Exec("AMT_RedirectionService", "TerminateSession", { "SessionType": SessionType }, callback_func); }
|
||
obj.AMT_RemoteAccessService_AddMpServer = function (AccessInfo, InfoFormat, Port, AuthMethod, Certificate, Username, Password, CN, callback_func) { obj.Exec("AMT_RemoteAccessService", "AddMpServer", { "AccessInfo": AccessInfo, "InfoFormat": InfoFormat, "Port": Port, "AuthMethod": AuthMethod, "Certificate": Certificate, "Username": Username, "Password": Password, "CN": CN }, callback_func); }
|
||
obj.AMT_RemoteAccessService_AddRemoteAccessPolicyRule = function (Trigger, TunnelLifeTime, ExtendedData, MpServer, callback_func) { obj.Exec("AMT_RemoteAccessService", "AddRemoteAccessPolicyRule", { "Trigger": Trigger, "TunnelLifeTime": TunnelLifeTime, "ExtendedData": ExtendedData, "MpServer": MpServer }, callback_func); }
|
||
obj.AMT_RemoteAccessService_CloseRemoteAccessConnection = function (_method_dummy, callback_func) { obj.Exec("AMT_RemoteAccessService", "CloseRemoteAccessConnection", { "_method_dummy": _method_dummy }, callback_func); }
|
||
obj.AMT_SetupAndConfigurationService_CommitChanges = function (_method_dummy, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "CommitChanges", { "_method_dummy": _method_dummy }, callback_func); }
|
||
obj.AMT_SetupAndConfigurationService_Unprovision = function (ProvisioningMode, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "Unprovision", { "ProvisioningMode": ProvisioningMode }, callback_func); }
|
||
obj.AMT_SetupAndConfigurationService_PartialUnprovision = function (_method_dummy, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "PartialUnprovision", { "_method_dummy": _method_dummy }, callback_func); }
|
||
obj.AMT_SetupAndConfigurationService_ResetFlashWearOutProtection = function (_method_dummy, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "ResetFlashWearOutProtection", { "_method_dummy": _method_dummy }, callback_func); }
|
||
obj.AMT_SetupAndConfigurationService_ExtendProvisioningPeriod = function (Duration, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "ExtendProvisioningPeriod", { "Duration": Duration }, callback_func); }
|
||
obj.AMT_SetupAndConfigurationService_SetMEBxPassword = function (Password, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "SetMEBxPassword", { "Password": Password }, callback_func); }
|
||
obj.AMT_SetupAndConfigurationService_SetTLSPSK = function (PID, PPS, callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "SetTLSPSK", { "PID": PID, "PPS": PPS }, callback_func); }
|
||
obj.AMT_SetupAndConfigurationService_GetProvisioningAuditRecord = function (callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "GetProvisioningAuditRecord", {}, callback_func); }
|
||
obj.AMT_SetupAndConfigurationService_GetUuid = function (callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "GetUuid", {}, callback_func); }
|
||
obj.AMT_SetupAndConfigurationService_GetUnprovisionBlockingComponents = function (callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "GetUnprovisionBlockingComponents", {}, callback_func); }
|
||
obj.AMT_SetupAndConfigurationService_GetProvisioningAuditRecordV2 = function (callback_func) { obj.Exec("AMT_SetupAndConfigurationService", "GetProvisioningAuditRecordV2", {}, callback_func); }
|
||
obj.AMT_SystemDefensePolicy_GetTimeout = function (callback_func) { obj.Exec("AMT_SystemDefensePolicy", "GetTimeout", {}, callback_func); }
|
||
obj.AMT_SystemDefensePolicy_SetTimeout = function (Timeout, callback_func) { obj.Exec("AMT_SystemDefensePolicy", "SetTimeout", { "Timeout": Timeout }, callback_func); }
|
||
obj.AMT_SystemDefensePolicy_UpdateStatistics = function (NetworkInterface, ResetOnRead, callback_func, tag, pri, selectors) { obj.Exec("AMT_SystemDefensePolicy", "UpdateStatistics", { "NetworkInterface": NetworkInterface, "ResetOnRead": ResetOnRead }, callback_func, tag, pri, selectors); }
|
||
obj.AMT_SystemPowerScheme_SetPowerScheme = function (callback_func, schemeInstanceId, tag) { obj.Exec("AMT_SystemPowerScheme", "SetPowerScheme", {}, callback_func, tag, 0, { "InstanceID": schemeInstanceId }); }
|
||
obj.AMT_TimeSynchronizationService_GetLowAccuracyTimeSynch = function (callback_func, tag) { obj.Exec("AMT_TimeSynchronizationService", "GetLowAccuracyTimeSynch", {}, callback_func, tag); }
|
||
obj.AMT_TimeSynchronizationService_SetHighAccuracyTimeSynch = function (Ta0, Tm1, Tm2, callback_func, tag) { obj.Exec("AMT_TimeSynchronizationService", "SetHighAccuracyTimeSynch", { "Ta0": Ta0, "Tm1": Tm1, "Tm2": Tm2 }, callback_func, tag); }
|
||
obj.AMT_UserInitiatedConnectionService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("AMT_UserInitiatedConnectionService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||
obj.AMT_WebUIService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("AMT_WebUIService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||
obj.AMT_WiFiPortConfigurationService_AddWiFiSettings = function (WiFiEndpoint, WiFiEndpointSettingsInput, IEEE8021xSettingsInput, ClientCredential, CACredential, callback_func) { obj.ExecWithXml("AMT_WiFiPortConfigurationService", "AddWiFiSettings", { "WiFiEndpoint": WiFiEndpoint, "WiFiEndpointSettingsInput": WiFiEndpointSettingsInput, "IEEE8021xSettingsInput": IEEE8021xSettingsInput, "ClientCredential": ClientCredential, "CACredential": CACredential }, callback_func); }
|
||
obj.AMT_WiFiPortConfigurationService_UpdateWiFiSettings = function (WiFiEndpointSettings, WiFiEndpointSettingsInput, IEEE8021xSettingsInput, ClientCredential, CACredential, callback_func) { obj.ExecWithXml("AMT_WiFiPortConfigurationService", "UpdateWiFiSettings", { "WiFiEndpointSettings": WiFiEndpointSettings, "WiFiEndpointSettingsInput": WiFiEndpointSettingsInput, "IEEE8021xSettingsInput": IEEE8021xSettingsInput, "ClientCredential": ClientCredential, "CACredential": CACredential }, callback_func); }
|
||
obj.AMT_WiFiPortConfigurationService_DeleteAllITProfiles = function (_method_dummy, callback_func) { obj.Exec("AMT_WiFiPortConfigurationService", "DeleteAllITProfiles", { "_method_dummy": _method_dummy }, callback_func); }
|
||
obj.AMT_WiFiPortConfigurationService_DeleteAllUserProfiles = function (_method_dummy, callback_func) { obj.Exec("AMT_WiFiPortConfigurationService", "DeleteAllUserProfiles", { "_method_dummy": _method_dummy }, callback_func); }
|
||
obj.CIM_Account_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_Account", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||
obj.CIM_AccountManagementService_CreateAccount = function (System, AccountTemplate, callback_func) { obj.Exec("CIM_AccountManagementService", "CreateAccount", { "System": System, "AccountTemplate": AccountTemplate }, callback_func); }
|
||
obj.CIM_BootConfigSetting_ChangeBootOrder = function (Source, callback_func) { obj.Exec("CIM_BootConfigSetting", "ChangeBootOrder", { "Source": Source }, callback_func); }
|
||
obj.CIM_BootService_SetBootConfigRole = function (BootConfigSetting, Role, callback_func) { obj.Exec("CIM_BootService", "SetBootConfigRole", { "BootConfigSetting": BootConfigSetting, "Role": Role }, callback_func, 0, 1); }
|
||
obj.CIM_Card_ConnectorPower = function (Connector, PoweredOn, callback_func) { obj.Exec("CIM_Card", "ConnectorPower", { "Connector": Connector, "PoweredOn": PoweredOn }, callback_func); }
|
||
obj.CIM_Card_IsCompatible = function (ElementToCheck, callback_func) { obj.Exec("CIM_Card", "IsCompatible", { "ElementToCheck": ElementToCheck }, callback_func); }
|
||
obj.CIM_Chassis_IsCompatible = function (ElementToCheck, callback_func) { obj.Exec("CIM_Chassis", "IsCompatible", { "ElementToCheck": ElementToCheck }, callback_func); }
|
||
obj.CIM_Fan_SetSpeed = function (DesiredSpeed, callback_func) { obj.Exec("CIM_Fan", "SetSpeed", { "DesiredSpeed": DesiredSpeed }, callback_func); }
|
||
obj.CIM_KVMRedirectionSAP_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_KVMRedirectionSAP", "RequestStateChange", { "RequestedState": RequestedState/*, "TimeoutPeriod": TimeoutPeriod */}, callback_func); }
|
||
obj.CIM_MediaAccessDevice_LockMedia = function (Lock, callback_func) { obj.Exec("CIM_MediaAccessDevice", "LockMedia", { "Lock": Lock }, callback_func); }
|
||
obj.CIM_MediaAccessDevice_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_MediaAccessDevice", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
|
||
obj.CIM_MediaAccessDevice_Reset = function (callback_func) { obj.Exec("CIM_MediaAccessDevice", "Reset", {}, callback_func); }
|
||
obj.CIM_MediaAccessDevice_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_MediaAccessDevice", "EnableDevice", { "Enabled": Enabled }, callback_func); }
|
||
obj.CIM_MediaAccessDevice_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_MediaAccessDevice", "OnlineDevice", { "Online": Online }, callback_func); }
|
||
obj.CIM_MediaAccessDevice_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_MediaAccessDevice", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
|
||
obj.CIM_MediaAccessDevice_SaveProperties = function (callback_func) { obj.Exec("CIM_MediaAccessDevice", "SaveProperties", {}, callback_func); }
|
||
obj.CIM_MediaAccessDevice_RestoreProperties = function (callback_func) { obj.Exec("CIM_MediaAccessDevice", "RestoreProperties", {}, callback_func); }
|
||
obj.CIM_MediaAccessDevice_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_MediaAccessDevice", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||
obj.CIM_PhysicalFrame_IsCompatible = function (ElementToCheck, callback_func) { obj.Exec("CIM_PhysicalFrame", "IsCompatible", { "ElementToCheck": ElementToCheck }, callback_func); }
|
||
obj.CIM_PhysicalPackage_IsCompatible = function (ElementToCheck, callback_func) { obj.Exec("CIM_PhysicalPackage", "IsCompatible", { "ElementToCheck": ElementToCheck }, callback_func); }
|
||
obj.CIM_PowerManagementService_RequestPowerStateChange = function (PowerState, ManagedElement, Time, TimeoutPeriod, callback_func) { obj.Exec("CIM_PowerManagementService", "RequestPowerStateChange", { "PowerState": PowerState, "ManagedElement": ManagedElement, "Time": Time, "TimeoutPeriod": TimeoutPeriod }, callback_func, 0, 1); }
|
||
obj.CIM_PowerSupply_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_PowerSupply", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
|
||
obj.CIM_PowerSupply_Reset = function (callback_func) { obj.Exec("CIM_PowerSupply", "Reset", {}, callback_func); }
|
||
obj.CIM_PowerSupply_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_PowerSupply", "EnableDevice", { "Enabled": Enabled }, callback_func); }
|
||
obj.CIM_PowerSupply_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_PowerSupply", "OnlineDevice", { "Online": Online }, callback_func); }
|
||
obj.CIM_PowerSupply_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_PowerSupply", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
|
||
obj.CIM_PowerSupply_SaveProperties = function (callback_func) { obj.Exec("CIM_PowerSupply", "SaveProperties", {}, callback_func); }
|
||
obj.CIM_PowerSupply_RestoreProperties = function (callback_func) { obj.Exec("CIM_PowerSupply", "RestoreProperties", {}, callback_func); }
|
||
obj.CIM_PowerSupply_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_PowerSupply", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||
obj.CIM_Processor_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_Processor", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
|
||
obj.CIM_Processor_Reset = function (callback_func) { obj.Exec("CIM_Processor", "Reset", {}, callback_func); }
|
||
obj.CIM_Processor_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_Processor", "EnableDevice", { "Enabled": Enabled }, callback_func); }
|
||
obj.CIM_Processor_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_Processor", "OnlineDevice", { "Online": Online }, callback_func); }
|
||
obj.CIM_Processor_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_Processor", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
|
||
obj.CIM_Processor_SaveProperties = function (callback_func) { obj.Exec("CIM_Processor", "SaveProperties", {}, callback_func); }
|
||
obj.CIM_Processor_RestoreProperties = function (callback_func) { obj.Exec("CIM_Processor", "RestoreProperties", {}, callback_func); }
|
||
obj.CIM_Processor_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_Processor", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||
obj.CIM_RecordLog_ClearLog = function (callback_func) { obj.Exec("CIM_RecordLog", "ClearLog", {}, callback_func); }
|
||
obj.CIM_RecordLog_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_RecordLog", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||
obj.CIM_RedirectionService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_RedirectionService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||
obj.CIM_Sensor_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_Sensor", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
|
||
obj.CIM_Sensor_Reset = function (callback_func) { obj.Exec("CIM_Sensor", "Reset", {}, callback_func); }
|
||
obj.CIM_Sensor_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_Sensor", "EnableDevice", { "Enabled": Enabled }, callback_func); }
|
||
obj.CIM_Sensor_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_Sensor", "OnlineDevice", { "Online": Online }, callback_func); }
|
||
obj.CIM_Sensor_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_Sensor", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
|
||
obj.CIM_Sensor_SaveProperties = function (callback_func) { obj.Exec("CIM_Sensor", "SaveProperties", {}, callback_func); }
|
||
obj.CIM_Sensor_RestoreProperties = function (callback_func) { obj.Exec("CIM_Sensor", "RestoreProperties", {}, callback_func); }
|
||
obj.CIM_Sensor_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_Sensor", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||
obj.CIM_StatisticalData_ResetSelectedStats = function (SelectedStatistics, callback_func) { obj.Exec("CIM_StatisticalData", "ResetSelectedStats", { "SelectedStatistics": SelectedStatistics }, callback_func); }
|
||
obj.CIM_Watchdog_KeepAlive = function (callback_func) { obj.Exec("CIM_Watchdog", "KeepAlive", {}, callback_func); }
|
||
obj.CIM_Watchdog_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_Watchdog", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
|
||
obj.CIM_Watchdog_Reset = function (callback_func) { obj.Exec("CIM_Watchdog", "Reset", {}, callback_func); }
|
||
obj.CIM_Watchdog_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_Watchdog", "EnableDevice", { "Enabled": Enabled }, callback_func); }
|
||
obj.CIM_Watchdog_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_Watchdog", "OnlineDevice", { "Online": Online }, callback_func); }
|
||
obj.CIM_Watchdog_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_Watchdog", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
|
||
obj.CIM_Watchdog_SaveProperties = function (callback_func) { obj.Exec("CIM_Watchdog", "SaveProperties", {}, callback_func); }
|
||
obj.CIM_Watchdog_RestoreProperties = function (callback_func) { obj.Exec("CIM_Watchdog", "RestoreProperties", {}, callback_func); }
|
||
obj.CIM_Watchdog_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_Watchdog", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||
obj.CIM_WiFiPort_SetPowerState = function (PowerState, Time, callback_func) { obj.Exec("CIM_WiFiPort", "SetPowerState", { "PowerState": PowerState, "Time": Time }, callback_func); }
|
||
obj.CIM_WiFiPort_Reset = function (callback_func) { obj.Exec("CIM_WiFiPort", "Reset", {}, callback_func); }
|
||
obj.CIM_WiFiPort_EnableDevice = function (Enabled, callback_func) { obj.Exec("CIM_WiFiPort", "EnableDevice", { "Enabled": Enabled }, callback_func); }
|
||
obj.CIM_WiFiPort_OnlineDevice = function (Online, callback_func) { obj.Exec("CIM_WiFiPort", "OnlineDevice", { "Online": Online }, callback_func); }
|
||
obj.CIM_WiFiPort_QuiesceDevice = function (Quiesce, callback_func) { obj.Exec("CIM_WiFiPort", "QuiesceDevice", { "Quiesce": Quiesce }, callback_func); }
|
||
obj.CIM_WiFiPort_SaveProperties = function (callback_func) { obj.Exec("CIM_WiFiPort", "SaveProperties", {}, callback_func); }
|
||
obj.CIM_WiFiPort_RestoreProperties = function (callback_func) { obj.Exec("CIM_WiFiPort", "RestoreProperties", {}, callback_func); }
|
||
obj.CIM_WiFiPort_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("CIM_WiFiPort", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||
obj.IPS_HostBasedSetupService_Setup = function (NetAdminPassEncryptionType, NetworkAdminPassword, McNonce, Certificate, SigningAlgorithm, DigitalSignature, callback_func) { obj.Exec("IPS_HostBasedSetupService", "Setup", { "NetAdminPassEncryptionType": NetAdminPassEncryptionType, "NetworkAdminPassword": NetworkAdminPassword, "McNonce": McNonce, "Certificate": Certificate, "SigningAlgorithm": SigningAlgorithm, "DigitalSignature": DigitalSignature }, callback_func); }
|
||
obj.IPS_HostBasedSetupService_AddNextCertInChain = function (NextCertificate, IsLeafCertificate, IsRootCertificate, callback_func) { obj.Exec("IPS_HostBasedSetupService", "AddNextCertInChain", { "NextCertificate": NextCertificate, "IsLeafCertificate": IsLeafCertificate, "IsRootCertificate": IsRootCertificate }, callback_func); }
|
||
obj.IPS_HostBasedSetupService_AdminSetup = function (NetAdminPassEncryptionType, NetworkAdminPassword, McNonce, SigningAlgorithm, DigitalSignature, callback_func) { obj.Exec("IPS_HostBasedSetupService", "AdminSetup", { "NetAdminPassEncryptionType": NetAdminPassEncryptionType, "NetworkAdminPassword": NetworkAdminPassword, "McNonce": McNonce, "SigningAlgorithm": SigningAlgorithm, "DigitalSignature": DigitalSignature }, callback_func); }
|
||
obj.IPS_HostBasedSetupService_UpgradeClientToAdmin = function (McNonce, SigningAlgorithm, DigitalSignature, callback_func) { obj.Exec("IPS_HostBasedSetupService", "UpgradeClientToAdmin", { "McNonce": McNonce, "SigningAlgorithm": SigningAlgorithm, "DigitalSignature": DigitalSignature }, callback_func); }
|
||
obj.IPS_HostBasedSetupService_DisableClientControlMode = function (_method_dummy, callback_func) { obj.Exec("IPS_HostBasedSetupService", "DisableClientControlMode", { "_method_dummy": _method_dummy }, callback_func); }
|
||
obj.IPS_KVMRedirectionSettingData_TerminateSession = function (callback_func) { obj.Exec("IPS_KVMRedirectionSettingData", "TerminateSession", {}, callback_func); }
|
||
obj.IPS_OptInService_StartOptIn = function (callback_func) { obj.Exec("IPS_OptInService", "StartOptIn", {}, callback_func); }
|
||
obj.IPS_OptInService_CancelOptIn = function (callback_func) { obj.Exec("IPS_OptInService", "CancelOptIn", {}, callback_func); }
|
||
obj.IPS_OptInService_SendOptInCode = function (OptInCode, callback_func) { obj.Exec("IPS_OptInService", "SendOptInCode", { "OptInCode": OptInCode }, callback_func); }
|
||
obj.IPS_OptInService_StartService = function (callback_func) { obj.Exec("IPS_OptInService", "StartService", {}, callback_func); }
|
||
obj.IPS_OptInService_StopService = function (callback_func) { obj.Exec("IPS_OptInService", "StopService", {}, callback_func); }
|
||
obj.IPS_OptInService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("IPS_OptInService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||
obj.IPS_ProvisioningRecordLog_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("IPS_ProvisioningRecordLog", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||
obj.IPS_ProvisioningRecordLog_ClearLog = function (_method_dummy, callback_func) { obj.Exec("IPS_ProvisioningRecordLog", "ClearLog", { "_method_dummy": _method_dummy }, callback_func); }
|
||
obj.IPS_SecIOService_RequestStateChange = function (RequestedState, TimeoutPeriod, callback_func) { obj.Exec("IPS_SecIOService", "RequestStateChange", { "RequestedState": RequestedState, "TimeoutPeriod": TimeoutPeriod }, callback_func); }
|
||
|
||
obj.AmtStatusToStr = function (code) { if (obj.AmtStatusCodes[code]) return obj.AmtStatusCodes[code]; else return "UNKNOWN_ERROR" }
|
||
obj.AmtStatusCodes = {
|
||
0x0000: "SUCCESS",
|
||
0x0001: "INTERNAL_ERROR",
|
||
0x0002: "NOT_READY",
|
||
0x0003: "INVALID_PT_MODE",
|
||
0x0004: "INVALID_MESSAGE_LENGTH",
|
||
0x0005: "TABLE_FINGERPRINT_NOT_AVAILABLE",
|
||
0x0006: "INTEGRITY_CHECK_FAILED",
|
||
0x0007: "UNSUPPORTED_ISVS_VERSION",
|
||
0x0008: "APPLICATION_NOT_REGISTERED",
|
||
0x0009: "INVALID_REGISTRATION_DATA",
|
||
0x000A: "APPLICATION_DOES_NOT_EXIST",
|
||
0x000B: "NOT_ENOUGH_STORAGE",
|
||
0x000C: "INVALID_NAME",
|
||
0x000D: "BLOCK_DOES_NOT_EXIST",
|
||
0x000E: "INVALID_BYTE_OFFSET",
|
||
0x000F: "INVALID_BYTE_COUNT",
|
||
0x0010: "NOT_PERMITTED",
|
||
0x0011: "NOT_OWNER",
|
||
0x0012: "BLOCK_LOCKED_BY_OTHER",
|
||
0x0013: "BLOCK_NOT_LOCKED",
|
||
0x0014: "INVALID_GROUP_PERMISSIONS",
|
||
0x0015: "GROUP_DOES_NOT_EXIST",
|
||
0x0016: "INVALID_MEMBER_COUNT",
|
||
0x0017: "MAX_LIMIT_REACHED",
|
||
0x0018: "INVALID_AUTH_TYPE",
|
||
0x0019: "AUTHENTICATION_FAILED",
|
||
0x001A: "INVALID_DHCP_MODE",
|
||
0x001B: "INVALID_IP_ADDRESS",
|
||
0x001C: "INVALID_DOMAIN_NAME",
|
||
0x001D: "UNSUPPORTED_VERSION",
|
||
0x001E: "REQUEST_UNEXPECTED",
|
||
0x001F: "INVALID_TABLE_TYPE",
|
||
0x0020: "INVALID_PROVISIONING_STATE",
|
||
0x0021: "UNSUPPORTED_OBJECT",
|
||
0x0022: "INVALID_TIME",
|
||
0x0023: "INVALID_INDEX",
|
||
0x0024: "INVALID_PARAMETER",
|
||
0x0025: "INVALID_NETMASK",
|
||
0x0026: "FLASH_WRITE_LIMIT_EXCEEDED",
|
||
0x0027: "INVALID_IMAGE_LENGTH",
|
||
0x0028: "INVALID_IMAGE_SIGNATURE",
|
||
0x0029: "PROPOSE_ANOTHER_VERSION",
|
||
0x002A: "INVALID_PID_FORMAT",
|
||
0x002B: "INVALID_PPS_FORMAT",
|
||
0x002C: "BIST_COMMAND_BLOCKED",
|
||
0x002D: "CONNECTION_FAILED",
|
||
0x002E: "CONNECTION_TOO_MANY",
|
||
0x002F: "RNG_GENERATION_IN_PROGRESS",
|
||
0x0030: "RNG_NOT_READY",
|
||
0x0031: "CERTIFICATE_NOT_READY",
|
||
0x0400: "DISABLED_BY_POLICY",
|
||
0x0800: "NETWORK_IF_ERROR_BASE",
|
||
0x0801: "UNSUPPORTED_OEM_NUMBER",
|
||
0x0802: "UNSUPPORTED_BOOT_OPTION",
|
||
0x0803: "INVALID_COMMAND",
|
||
0x0804: "INVALID_SPECIAL_COMMAND",
|
||
0x0805: "INVALID_HANDLE",
|
||
0x0806: "INVALID_PASSWORD",
|
||
0x0807: "INVALID_REALM",
|
||
0x0808: "STORAGE_ACL_ENTRY_IN_USE",
|
||
0x0809: "DATA_MISSING",
|
||
0x080A: "DUPLICATE",
|
||
0x080B: "EVENTLOG_FROZEN",
|
||
0x080C: "PKI_MISSING_KEYS",
|
||
0x080D: "PKI_GENERATING_KEYS",
|
||
0x080E: "INVALID_KEY",
|
||
0x080F: "INVALID_CERT",
|
||
0x0810: "CERT_KEY_NOT_MATCH",
|
||
0x0811: "MAX_KERB_DOMAIN_REACHED",
|
||
0x0812: "UNSUPPORTED",
|
||
0x0813: "INVALID_PRIORITY",
|
||
0x0814: "NOT_FOUND",
|
||
0x0815: "INVALID_CREDENTIALS",
|
||
0x0816: "INVALID_PASSPHRASE",
|
||
0x0818: "NO_ASSOCIATION",
|
||
0x081B: "AUDIT_FAIL",
|
||
0x081C: "BLOCKING_COMPONENT",
|
||
0x0821: "USER_CONSENT_REQUIRED",
|
||
0x1000: "APP_INTERNAL_ERROR",
|
||
0x1001: "NOT_INITIALIZED",
|
||
0x1002: "LIB_VERSION_UNSUPPORTED",
|
||
0x1003: "INVALID_PARAM",
|
||
0x1004: "RESOURCES",
|
||
0x1005: "HARDWARE_ACCESS_ERROR",
|
||
0x1006: "REQUESTOR_NOT_REGISTERED",
|
||
0x1007: "NETWORK_ERROR",
|
||
0x1008: "PARAM_BUFFER_TOO_SHORT",
|
||
0x1009: "COM_NOT_INITIALIZED_IN_THREAD",
|
||
0x100A: "URL_REQUIRED"
|
||
}
|
||
|
||
//
|
||
// Methods used for getting the event log
|
||
//
|
||
|
||
obj.GetMessageLog = function (func, tag) {
|
||
obj.AMT_MessageLog_PositionToFirstRecord(_GetMessageLog0, [func, tag, []]);
|
||
}
|
||
function _GetMessageLog0(stack, name, responses, status, tag) {
|
||
if (status != 200 || responses.Body["ReturnValue"] != '0') { tag[0](obj, null, tag[2]); return; }
|
||
obj.AMT_MessageLog_GetRecords(responses.Body["IterationIdentifier"], 390, _GetMessageLog1, tag);
|
||
}
|
||
function _GetMessageLog1(stack, name, responses, status, tag) {
|
||
if (status != 200 || responses.Body["ReturnValue"] != '0') { tag[0](obj, null, tag[2]); return; }
|
||
var i, j, x, e, AmtMessages = tag[2], t = new Date(), TimeStamp, ra = responses.Body["RecordArray"];
|
||
if (typeof ra === 'string') { responses.Body["RecordArray"] = [responses.Body["RecordArray"]]; }
|
||
|
||
for (i in ra) {
|
||
e = null;
|
||
try { e = window.atob(ra[i]); } catch (ex) { }
|
||
if (e != null) {
|
||
TimeStamp = ReadIntX(e, 0);
|
||
if ((TimeStamp > 0) && (TimeStamp < 0xFFFFFFFF)) {
|
||
x = { 'DeviceAddress': e.charCodeAt(4), 'EventSensorType': e.charCodeAt(5), 'EventType': e.charCodeAt(6), 'EventOffset': e.charCodeAt(7), 'EventSourceType': e.charCodeAt(8), 'EventSeverity': e.charCodeAt(9), 'SensorNumber': e.charCodeAt(10), 'Entity': e.charCodeAt(11), 'EntityInstance': e.charCodeAt(12), 'EventData': [], 'Time': new Date((TimeStamp + (t.getTimezoneOffset() * 60)) * 1000) };
|
||
for (j = 13; j < 21; j++) { x['EventData'].push(e.charCodeAt(j)); }
|
||
x['EntityStr'] = _SystemEntityTypes[x['Entity']];
|
||
x['Desc'] = _GetEventDetailStr(x['EventSensorType'], x['EventOffset'], x['EventData'], x['Entity']);
|
||
if (!x['EntityStr']) x['EntityStr'] = "Unknown";
|
||
AmtMessages.push(x);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (responses.Body["NoMoreRecords"] != true) { obj.AMT_MessageLog_GetRecords(responses.Body["IterationIdentifier"], 390, _GetMessageLog1, [tag[0], AmtMessages, tag[2]]); } else { tag[0](obj, AmtMessages, tag[2]); }
|
||
}
|
||
|
||
var _EventTrapSourceTypes = "Platform firmware (e.g. BIOS)|SMI handler|ISV system management software|Alert ASIC|IPMI|BIOS vendor|System board set vendor|System integrator|Third party add-in|OSV|NIC|System management card".split('|');
|
||
var _SystemFirmwareError = "Unspecified.|No system memory is physically installed in the system.|No usable system memory, all installed memory has experienced an unrecoverable failure.|Unrecoverable hard-disk/ATAPI/IDE device failure.|Unrecoverable system-board failure.|Unrecoverable diskette subsystem failure.|Unrecoverable hard-disk controller failure.|Unrecoverable PS/2 or USB keyboard failure.|Removable boot media not found.|Unrecoverable video controller failure.|No video device detected.|Firmware (BIOS) ROM corruption detected.|CPU voltage mismatch (processors that share same supply have mismatched voltage requirements)|CPU speed matching failure".split('|');
|
||
var _SystemFirmwareProgress = "Unspecified.|Memory initialization.|Starting hard-disk initialization and test|Secondary processor(s) initialization|User authentication|User-initiated system setup|USB resource configuration|PCI resource configuration|Option ROM initialization|Video initialization|Cache initialization|SM Bus initialization|Keyboard controller initialization|Embedded controller/management controller initialization|Docking station attachment|Enabling docking station|Docking station ejection|Disabling docking station|Calling operating system wake-up vector|Starting operating system boot process|Baseboard or motherboard initialization|reserved|Floppy initialization|Keyboard test|Pointing device test|Primary processor initialization".split('|');
|
||
var _SystemEntityTypes = "Unspecified|Other|Unknown|Processor|Disk|Peripheral|System management module|System board|Memory module|Processor module|Power supply|Add in card|Front panel board|Back panel board|Power system board|Drive backplane|System internal expansion board|Other system board|Processor board|Power unit|Power module|Power management board|Chassis back panel board|System chassis|Sub chassis|Other chassis board|Disk drive bay|Peripheral bay|Device bay|Fan cooling|Cooling unit|Cable interconnect|Memory device|System management software|BIOS|Intel(r) ME|System bus|Group|Intel(r) ME|External environment|Battery|Processing blade|Connectivity switch|Processor/memory module|I/O module|Processor I/O module|Management controller firmware|IPMI channel|PCI bus|PCI express bus|SCSI bus|SATA/SAS bus|Processor front side bus".split('|');
|
||
obj.RealmNames = "||Redirection|PT Administration|Hardware Asset|Remote Control|Storage|Event Manager|Storage Admin|Agent Presence Local|Agent Presence Remote|Circuit Breaker|Network Time|General Information|Firmware Update|EIT|LocalUN|Endpoint Access Control|Endpoint Access Control Admin|Event Log Reader|Audit Log|ACL Realm|||Local System".split('|');
|
||
obj.WatchdogCurrentStates = { 1: 'Not Started', 2: 'Stopped', 4: 'Running', 8: 'Expired', 16: 'Suspended' };
|
||
|
||
function _GetEventDetailStr(eventSensorType, eventOffset, eventDataField, entity) {
|
||
|
||
if (eventSensorType == 15)
|
||
{
|
||
if (eventDataField[0] == 235) return "Invalid Data";
|
||
if (eventOffset == 0) return _SystemFirmwareError[eventDataField[1]];
|
||
return _SystemFirmwareProgress[eventDataField[1]];
|
||
}
|
||
|
||
if (eventSensorType == 18 && eventDataField[0] == 170) // System watchdog event
|
||
{
|
||
return "Agent watchdog " + char2hex(eventDataField[4]) + char2hex(eventDataField[3]) + char2hex(eventDataField[2]) + char2hex(eventDataField[1]) + "-" + char2hex(eventDataField[6]) + char2hex(eventDataField[5]) + "-... changed to " + obj.WatchdogCurrentStates[eventDataField[7]];
|
||
}
|
||
|
||
/*
|
||
if (eventSensorType == 5 && eventOffset == 0) // System chassis
|
||
{
|
||
return "Case intrusion";
|
||
}
|
||
|
||
if (eventSensorType == 192 && eventOffset == 0 && eventDataField[0] == 170 && eventDataField[1] == 48)
|
||
{
|
||
if (eventDataField[2] == 0) return "A remote Serial Over LAN session was established.";
|
||
if (eventDataField[2] == 1) return "Remote Serial Over LAN session finished. User control was restored.";
|
||
if (eventDataField[2] == 2) return "A remote IDE-Redirection session was established.";
|
||
if (eventDataField[2] == 3) return "Remote IDE-Redirection session finished. User control was restored.";
|
||
}
|
||
|
||
if (eventSensorType == 36)
|
||
{
|
||
long handle = ((long)(eventDataField[1]) << 24) + ((long)(eventDataField[2]) << 16) + ((long)(eventDataField[3]) << 8) + (long)(eventDataField[4]);
|
||
string nic = string.Format("#{0}", eventDataField[0]);
|
||
if (eventDataField[0] == 0xAA) nic = "wired"; // TODO: Add wireless *****
|
||
//if (eventDataField[0] == 0xAA) nic = "wireless";
|
||
|
||
if (handle == 4294967293) { return string.Format("All received packet filter was matched on {0} interface.", nic); }
|
||
if (handle == 4294967292) { return string.Format("All outbound packet filter was matched on {0} interface.", nic); }
|
||
if (handle == 4294967290) { return string.Format("Spoofed packet filter was matched on {0} interface.", nic); }
|
||
return string.Format("Filter {0} was matched on {1} interface.", handle, nic);
|
||
}
|
||
|
||
if (eventSensorType == 192)
|
||
{
|
||
if (eventDataField[2] == 0) return "Security policy invoked. Some or all network traffic (TX) was stopped.";
|
||
if (eventDataField[2] == 2) return "Security policy invoked. Some or all network traffic (RX) was stopped.";
|
||
return "Security policy invoked.";
|
||
}
|
||
|
||
if (eventSensorType == 193)
|
||
{
|
||
if (eventDataField[0] == 0xAA && eventDataField[1] == 0x30 && eventDataField[2] == 0x00 && eventDataField[3] == 0x00) { return "User request for remote connection."; }
|
||
if (eventDataField[0] == 0xAA && eventDataField[1] == 0x20 && eventDataField[2] == 0x03 && eventDataField[3] == 0x01) { return "EAC error: attempt to get posture while NAC in Intel<65> AMT is disabled."; // eventDataField = 0xAA20030100000000 }
|
||
if (eventDataField[0] == 0xAA && eventDataField[1] == 0x20 && eventDataField[2] == 0x04 && eventDataField[3] == 0x00) { return "Certificate revoked. "; }
|
||
}
|
||
*/
|
||
|
||
if (eventSensorType == 6) return "Authentication failed " + (eventDataField[1] + (eventDataField[2] << 8)) + " times. The system may be under attack.";
|
||
if (eventSensorType == 30) return "No bootable media";
|
||
if (eventSensorType == 32) return "Operating system lockup or power interrupt";
|
||
if (eventSensorType == 35) return "System boot failure";
|
||
if (eventSensorType == 37) return "System firmware started (at least one CPU is properly executing).";
|
||
return "Unknown Sensor Type #" + eventSensorType;
|
||
}
|
||
|
||
|
||
return obj;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
// TinyMD5 from https://github.com/jbt/js-crypto
|
||
|
||
// Perform MD5 setup
|
||
var md5_k = [];
|
||
for (var i = 0; i < 64;) { md5_k[i] = 0 | (Math.abs(Math.sin(++i)) * 4294967296); }
|
||
|
||
// Perform MD5 on raw string and return hex
|
||
function hex_md5(str) {
|
||
var b, c, d, j,
|
||
x = [],
|
||
str2 = unescape(encodeURI(str)),
|
||
a = str2.length,
|
||
h = [b = 1732584193, c = -271733879, ~b, ~c],
|
||
i = 0;
|
||
|
||
for (; i <= a;) x[i >> 2] |= (str2.charCodeAt(i) || 128) << 8 * (i++ % 4);
|
||
|
||
x[str = (a + 8 >> 6) * 16 + 14] = a * 8;
|
||
i = 0;
|
||
|
||
for (; i < str; i += 16) {
|
||
a = h; j = 0;
|
||
for (; j < 64;) {
|
||
a = [
|
||
d = a[3],
|
||
((b = a[1] | 0) +
|
||
((d = (
|
||
(a[0] +
|
||
[
|
||
b & (c = a[2]) | ~b & d,
|
||
d & b | ~d & c,
|
||
b ^ c ^ d,
|
||
c ^ (b | ~d)
|
||
][a = j >> 4]
|
||
) +
|
||
(md5_k[j] +
|
||
(x[[
|
||
j,
|
||
5 * j + 1,
|
||
3 * j + 5,
|
||
7 * j
|
||
][a] % 16 + i] | 0)
|
||
)
|
||
)) << (a = [
|
||
7, 12, 17, 22,
|
||
5, 9, 14, 20,
|
||
4, 11, 16, 23,
|
||
6, 10, 15, 21
|
||
][4 * a + j++ % 4]) | d >>> 32 - a)
|
||
),
|
||
b,
|
||
c
|
||
];
|
||
}
|
||
for (j = 4; j;) h[--j] = h[j] + a[j];
|
||
}
|
||
|
||
str = '';
|
||
for (; j < 32;) str += ((h[j >> 3] >> ((1 ^ j++ & 7) * 4)) & 15).toString(16);
|
||
return str;
|
||
}
|
||
|
||
|
||
// Perform MD5 on raw string and return raw string result
|
||
function rstr_md5(str) { return hex2rstr(hex_md5(str)); }
|
||
|
||
/*
|
||
Convert arguments into selector set and body XML. Used by AMT_WiFiPortConfigurationService_UpdateWiFiSettings.
|
||
args = {
|
||
"WiFiEndpoint": {
|
||
__parameterType: 'reference',
|
||
__resourceUri: 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_WiFiEndpoint',
|
||
Name: 'WiFi Endpoint 0'
|
||
},
|
||
"WiFiEndpointSettingsInput":
|
||
{
|
||
__parameterType: 'instance',
|
||
__namespace: 'http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_WiFiEndpointSettings',
|
||
ElementName: document.querySelector('#editProfile-profileName').value,
|
||
InstanceID: 'Intel(r) AMT:WiFi Endpoint Settings ' + document.querySelector('#editProfile-profileName').value,
|
||
AuthenticationMethod: document.querySelector('#editProfile-networkAuthentication').value,
|
||
//BSSType: 3, // Intel(r) AMT supports only infrastructure networks
|
||
EncryptionMethod: document.querySelector('#editProfile-encryption').value,
|
||
SSID: document.querySelector('#editProfile-networkName').value,
|
||
Priority: 100,
|
||
PSKPassPhrase: document.querySelector('#editProfile-passPhrase').value
|
||
},
|
||
"IEEE8021xSettingsInput": null,
|
||
"ClientCredential": null,
|
||
"CACredential": null
|
||
},
|
||
*/
|
||
function execArgumentsToXml(args) {
|
||
if(args === undefined || args === null) return null;
|
||
|
||
var result = '';
|
||
for(var argName in args) {
|
||
var arg = args[argName];
|
||
if(!arg) continue;
|
||
if(arg['__parameterType'] === 'reference') result += referenceToXml(argName, arg);
|
||
else result += instanceToXml(argName, arg);
|
||
//if(arg['__isInstance']) result += instanceToXml(argName, arg);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* Convert JavaScript object into XML
|
||
|
||
<r:WiFiEndpointSettingsInput xmlns:q="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_WiFiEndpointSettings">
|
||
<q:ElementName>Wireless-Profile-Admin</q:ElementName>
|
||
<q:InstanceID>Intel(r) AMT:WiFi Endpoint Settings Wireless-Profile-Admin</q:InstanceID>
|
||
<q:AuthenticationMethod>6</q:AuthenticationMethod>
|
||
<q:EncryptionMethod>4</q:EncryptionMethod>
|
||
<q:Priority>100</q:Priority>
|
||
<q:PSKPassPhrase>P@ssw0rd</q:PSKPassPhrase>
|
||
</r:WiFiEndpointSettingsInput>
|
||
*/
|
||
function instanceToXml(instanceName, inInstance) {
|
||
if(inInstance === undefined || inInstance === null) return null;
|
||
|
||
var hasNamespace = !!inInstance['__namespace'];
|
||
var startTag = hasNamespace ? '<q:' : '<';
|
||
var endTag = hasNamespace ? '</q:' : '</';
|
||
var namespaceDef = hasNamespace ? (' xmlns:q="' + inInstance['__namespace'] + '"' ): '';
|
||
var result = '<r:' + instanceName + namespaceDef + '>';
|
||
for(var prop in inInstance) {
|
||
if (!inInstance.hasOwnProperty(prop) || prop.indexOf('__') === 0) continue;
|
||
|
||
if (typeof inInstance[prop] === 'function' || Array.isArray(inInstance[prop]) ) continue;
|
||
|
||
if (typeof inInstance[prop] === 'object') {
|
||
//result += startTag + prop +'>' + instanceToXml('prop', inInstance[prop]) + endTag + prop +'>';
|
||
console.error('only convert one level down...');
|
||
}
|
||
else {
|
||
result += startTag + prop +'>' + inInstance[prop].toString() + endTag + prop +'>';
|
||
}
|
||
}
|
||
result += '</r:' + instanceName + '>';
|
||
return result;
|
||
}
|
||
|
||
|
||
/**
|
||
* Convert a selector set into XML. Expect no nesting.
|
||
* {
|
||
* selectorName : selectorValue,
|
||
* selectorName : selectorValue,
|
||
* ... ...
|
||
* }
|
||
|
||
<r:WiFiEndpoint>
|
||
<a:Address>http://192.168.1.103:16992/wsman</a:Address>
|
||
<a:ReferenceParameters>
|
||
<w:ResourceURI>http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_WiFiEndpoint</w:ResourceURI>
|
||
<w:SelectorSet>
|
||
<w:Selector Name="Name">WiFi Endpoint 0</w:Selector>
|
||
</w:SelectorSet>
|
||
</a:ReferenceParameters>
|
||
</r:WiFiEndpoint>
|
||
|
||
*/
|
||
function referenceToXml(referenceName, inReference) {
|
||
if(inReference === undefined || inReference === null ) return null;
|
||
|
||
var result = '<r:' + referenceName + '><a:Address>/wsman</a:Address><a:ReferenceParameters><w:ResourceURI>'+ inReference['__resourceUri']+'</w:ResourceURI><w:SelectorSet>';
|
||
for(var selectorName in inReference) {
|
||
if (!inReference.hasOwnProperty(selectorName) || selectorName.indexOf('__') === 0) continue;
|
||
|
||
if (typeof inReference[selectorName] === 'function' ||
|
||
typeof inReference[selectorName] === 'object' ||
|
||
Array.isArray(inReference[selectorName]) )
|
||
continue;
|
||
|
||
result += '<w:Selector Name="' + selectorName +'">' + inReference[selectorName].toString() + '</w:Selector>';
|
||
}
|
||
|
||
result += '</w:SelectorSet></a:ReferenceParameters></r:' + referenceName + '>';
|
||
return result;
|
||
}
|
||
|
||
// Convert a byte array of SID into string
|
||
function GetSidString(sid) {
|
||
var r = "S-" + sid.charCodeAt(0) + "-" + sid.charCodeAt(7);
|
||
for (var i = 2; i < (sid.length / 4) ; i++) r += "-" + ReadIntX(sid, i * 4);
|
||
return r;
|
||
}
|
||
|
||
// Convert a SID readable string into bytes
|
||
function GetSidByteArray(sidString) {
|
||
if (!sidString || sidString == null) return null;
|
||
var sidParts = sidString.split('-');
|
||
|
||
// Make sure the SID has at least 4 parts and starts with 'S'
|
||
if (sidParts.length < 4 || (sidParts[0] != 's' && sidParts[0] != 'S')) return null;
|
||
|
||
// Check that each part of the SID is really an integer
|
||
for (var i = 1; i < sidParts.length; i++) { var y = parseInt(sidParts[i]); if (y != sidParts[i]) return null; sidParts[i] = y; }
|
||
|
||
// Version (8 bit) + Id count (8 bit) + 48 bit in big endian -- DO NOT use bitwise right shift operator. JavaScript converts the number into a 32 bit integer before shifting. In real world, it's highly likely this part is always 0.
|
||
var r = String.fromCharCode(sidParts[1]) + String.fromCharCode(sidParts.length - 3) + ShortToStr(Math.floor(sidParts[2] / Math.pow(2, 32))) + IntToStr((sidParts[2]) & 0xFFFF);
|
||
|
||
// the rest are in 32 bit in little endian
|
||
for (var i = 3; i < sidParts.length; i++) r += IntToStrX(sidParts[i]);
|
||
return r;
|
||
}
|
||
/**
|
||
* @description Intel(r) AMT WSMAN Stack
|
||
* @author Ylian Saint-Hilaire
|
||
* @version v0.2.0
|
||
*/
|
||
|
||
// Construct a MeshServer object
|
||
var WsmanStackCreateService = function (host, port, user, pass, tls, extra) {
|
||
var obj = {};
|
||
//obj.onDebugMessage = null; // Set to a function if you want to get debug messages.
|
||
obj.NextMessageId = 1; // Next message number, used to label WSMAN calls.
|
||
obj.Address = '/wsman';
|
||
obj.comm = CreateWsmanComm(host, port, user, pass, tls, extra);
|
||
|
||
obj.PerformAjax = function (postdata, callback, tag, pri, namespaces) {
|
||
if (namespaces == undefined) namespaces = '';
|
||
obj.comm.PerformAjax('<?xml version=\"1.0\" encoding=\"utf-8\"?><Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd" xmlns=\"http://www.w3.org/2003/05/soap-envelope\" ' + namespaces + '><Header><a:Action>' + postdata, function (data, status, tag) {
|
||
if (status != 200) { callback(obj, null, { Header: { HttpError: status } }, status, tag); return; }
|
||
var wsresponse = obj.ParseWsman(data);
|
||
if (!wsresponse || wsresponse == null) { callback(obj, null, { Header: { HttpError: status } }, 601, tag); } else { callback(obj, wsresponse.Header["ResourceURI"], wsresponse, 200, tag); }
|
||
}, tag, pri);
|
||
}
|
||
|
||
// Private method
|
||
//obj.Debug = function (msg) { /*console.log(msg);*/ }
|
||
|
||
// Cancel all pending queries with given status
|
||
obj.CancelAllQueries = function (s) { obj.comm.CancelAllQueries(s); }
|
||
|
||
// Get the last element of a URI string
|
||
obj.GetNameFromUrl = function (resuri) {
|
||
var x = resuri.lastIndexOf("/");
|
||
return (x == -1)?resuri:resuri.substring(x + 1);
|
||
}
|
||
|
||
// Perform a WSMAN Subscribe operation
|
||
obj.ExecSubscribe = function (resuri, delivery, url, callback, tag, pri, selectors, opaque, user, pass) {
|
||
var digest = "", digest2 = "";
|
||
if (user != undefined && pass != undefined) { digest = '<t:IssuedTokens><t:RequestSecurityTokenResponse><t:TokenType>http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken</t:TokenType><t:RequestedSecurityToken><se:UsernameToken><se:Username>' + user + '</se:Username><se:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd#PasswordText">' + pass + '</se:Password></se:UsernameToken></t:RequestedSecurityToken></t:RequestSecurityTokenResponse></t:IssuedTokens>'; digest2 = '<Auth Profile="http://schemas.xmlsoap.org/ws/2004/08/eventing/DeliveryModes/secprofile/http/digest"/>'; }
|
||
if (opaque != undefined && opaque != null) { opaque = '<a:ReferenceParameters>' + opaque + '</a:ReferenceParameters>'; } else { opaque = ""; }
|
||
var data = "http://schemas.xmlsoap.org/ws/2004/08/eventing/Subscribe</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>" + _PutObjToSelectorsXml(selectors) + digest + '</Header><Body><e:Subscribe><e:Delivery Mode="http://schemas.dmtf.org/wbem/wsman/1/wsman/' + delivery + '"><e:NotifyTo><a:Address>' + url + '</a:Address></e:NotifyTo>' + digest2 + '</e:Delivery><e:Expires>PT0.000000S</e:Expires></e:Subscribe>';
|
||
obj.PerformAjax(data + "</Body></Envelope>", callback, tag, pri, 'xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing" xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust" xmlns:se="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:m="http://x.com"');
|
||
}
|
||
|
||
// Perform a WSMAN UnSubscribe operation
|
||
obj.ExecUnSubscribe = function (resuri, callback, tag, pri, selectors) {
|
||
var data = "http://schemas.xmlsoap.org/ws/2004/08/eventing/Unsubscribe</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>" + _PutObjToSelectorsXml(selectors) + '</Header><Body><e:Unsubscribe/>';
|
||
obj.PerformAjax(data + "</Body></Envelope>", callback, tag, pri, 'xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing"');
|
||
}
|
||
|
||
// Perform a WSMAN PUT operation
|
||
obj.ExecPut = function (resuri, putobj, callback, tag, pri, selectors) {
|
||
var data = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Put</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60.000S</w:OperationTimeout>" + _PutObjToSelectorsXml(selectors) + '</Header><Body>' + _PutObjToBodyXml(resuri, putobj);
|
||
obj.PerformAjax(data + "</Body></Envelope>", callback, tag, pri);
|
||
}
|
||
|
||
// Perform a WSMAN CREATE operation
|
||
obj.ExecCreate = function (resuri, putobj, callback, tag, pri, selectors) {
|
||
var objname = obj.GetNameFromUrl(resuri);
|
||
var data = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Create</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout>" + _PutObjToSelectorsXml(selectors) + "</Header><Body><g:" + objname + " xmlns:g=\"" + resuri + "\">";
|
||
for (var n in putobj) { data += "<g:" + n + ">" + putobj[n] + "</g:" + n + ">" }
|
||
obj.PerformAjax(data + "</g:" + objname + "></Body></Envelope>", callback, tag, pri);
|
||
}
|
||
|
||
// Perform a WSMAN CREATE operation
|
||
obj.ExecCreateXml = function (resuri, argsxml, callback, tag, pri) {
|
||
var objname = obj.GetNameFromUrl(resuri), selector = "";
|
||
obj.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/transfer/Create</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60.000S</w:OperationTimeout></Header><Body><r:" + objname + " xmlns:r=\"" + resuri + "\">" + argsxml + "</r:" + objname + "></Body></Envelope>", callback, tag, pri);
|
||
}
|
||
|
||
// Perform a WSMAN DELETE operation
|
||
obj.ExecDelete = function (resuri, putobj, callback, tag, pri) {
|
||
var data = "http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout>" + _PutObjToSelectorsXml(putobj) + "</Header><Body /></Envelope>";
|
||
obj.PerformAjax(data, callback, tag, pri);
|
||
}
|
||
|
||
// Perform a WSMAN GET operation
|
||
obj.ExecGet = function (resuri, callback, tag, pri) {
|
||
obj.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/transfer/Get</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout></Header><Body /></Envelope>", callback, tag, pri);
|
||
}
|
||
|
||
// Perform a WSMAN method call operation
|
||
obj.ExecMethod = function (resuri, method, args, callback, tag, pri, selectors) {
|
||
var argsxml = "";
|
||
for (var i in args) { if (args[i] != null) { if (Array.isArray(args[i])) { for (var x in args[i]) { argsxml += "<r:" + i + ">" + args[i][x] + "</r:" + i + ">"; } } else { argsxml += "<r:" + i + ">" + args[i] + "</r:" + i + ">"; } } }
|
||
obj.ExecMethodXml(resuri, method, argsxml, callback, tag, pri, selectors);
|
||
}
|
||
|
||
// Perform a WSMAN method call operation. The arguments are already formatted in XML.
|
||
obj.ExecMethodXml = function (resuri, method, argsxml, callback, tag, pri, selectors) {
|
||
obj.PerformAjax(resuri + "/" + method + "</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout>" + _PutObjToSelectorsXml(selectors) + "</Header><Body><r:" + method + '_INPUT' + " xmlns:r=\"" + resuri + "\">" + argsxml + "</r:" + method + "_INPUT></Body></Envelope>", callback, tag, pri);
|
||
}
|
||
|
||
// Perform a WSMAN ENUM operation
|
||
obj.ExecEnum = function (resuri, callback, tag, pri) {
|
||
obj.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/enumeration/Enumerate</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout></Header><Body><Enumerate xmlns=\"http://schemas.xmlsoap.org/ws/2004/09/enumeration\" /></Body></Envelope>", callback, tag, pri);
|
||
}
|
||
|
||
// Perform a WSMAN PULL operation
|
||
obj.ExecPull = function (resuri, enumctx, callback, tag, pri) {
|
||
obj.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/enumeration/Pull</a:Action><a:To>" + obj.Address + "</a:To><w:ResourceURI>" + resuri + "</w:ResourceURI><a:MessageID>" + (obj.NextMessageId++) + "</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><w:OperationTimeout>PT60S</w:OperationTimeout></Header><Body><Pull xmlns=\"http://schemas.xmlsoap.org/ws/2004/09/enumeration\"><EnumerationContext>" + enumctx + "</EnumerationContext><MaxElements>999</MaxElements><MaxCharacters>99999</MaxCharacters></Pull></Body></Envelope>", callback, tag, pri);
|
||
}
|
||
|
||
// Private method
|
||
obj.ParseWsman = function (xml) {
|
||
try {
|
||
if (!xml.childNodes) xml = _turnToXml(xml);
|
||
var r = { Header:{} }, header = xml.getElementsByTagName("Header")[0], t;
|
||
if (!header) header = xml.getElementsByTagName("a:Header")[0];
|
||
if (!header) return null;
|
||
for (var i = 0; i < header.childNodes.length; i++) {
|
||
var child = header.childNodes[i];
|
||
r.Header[child.localName] = child.textContent;
|
||
}
|
||
var body = xml.getElementsByTagName("Body")[0];
|
||
if (!body) body = xml.getElementsByTagName("a:Body")[0];
|
||
if (!body) return null;
|
||
if (body.childNodes.length > 0) {
|
||
t = body.childNodes[0].localName;
|
||
if (t.indexOf("_OUTPUT") == t.length - 7) { t = t.substring(0, t.length - 7); }
|
||
r.Header['Method'] = t;
|
||
r.Body = _ParseWsmanRec(body.childNodes[0]);
|
||
}
|
||
return r;
|
||
} catch (e) {
|
||
console.log("Unable to parse XML: " + xml);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
// Private method
|
||
function _ParseWsmanRec(node) {
|
||
var data, r = {};
|
||
for (var i = 0; i < node.childNodes.length; i++) {
|
||
var child = node.childNodes[i];
|
||
if (child.childElementCount == 0) { data = child.textContent; } else { data = _ParseWsmanRec(child); }
|
||
if (data == 'true') data = true; // Convert 'true' into true
|
||
if (data == 'false') data = false; // Convert 'false' into false
|
||
|
||
var childObj = data;
|
||
if (child.attributes.length > 0) {
|
||
childObj = {'Value': data };
|
||
for(var j = 0; j < child.attributes.length; j++) {
|
||
childObj['@' + child.attributes[j].name] = child.attributes[j].value;
|
||
}
|
||
}
|
||
|
||
if (r[child.localName] instanceof Array) { r[child.localName].push(childObj); }
|
||
else if (r[child.localName] == undefined) { r[child.localName] = childObj; }
|
||
else { r[child.localName] = [r[child.localName], childObj]; }
|
||
}
|
||
return r;
|
||
}
|
||
|
||
function _PutObjToBodyXml(resuri, putObj) {
|
||
if(!resuri || putObj === undefined || putObj === null) return '';
|
||
var objname = obj.GetNameFromUrl(resuri);
|
||
var result = '<r:' + objname + ' xmlns:r="' + resuri + '">';
|
||
|
||
for (var prop in putObj) {
|
||
if (!putObj.hasOwnProperty(prop) || prop.indexOf('__') === 0 || prop.indexOf('@') === 0) continue;
|
||
if (putObj[prop] === undefined || putObj[prop] === null || typeof putObj[prop] === 'function') continue;
|
||
if (typeof putObj[prop] === 'object' && putObj[prop]['ReferenceParameters']) {
|
||
result += '<r:' + prop + '><a:Address>' + putObj[prop].Address + '</a:Address><a:ReferenceParameters><w:ResourceURI>' + putObj[prop]['ReferenceParameters']["ResourceURI"] + '</w:ResourceURI><w:SelectorSet>';
|
||
var selectorArray = putObj[prop]['ReferenceParameters']['SelectorSet']['Selector'];
|
||
if (Array.isArray(selectorArray)) {
|
||
for (var i=0; i< selectorArray.length; i++) {
|
||
result += '<w:Selector' + _ObjectToXmlAttributes(selectorArray[i]) + '>' + selectorArray[i]['Value'] + '</w:Selector>';
|
||
}
|
||
}
|
||
else {
|
||
result += '<w:Selector' + _ObjectToXmlAttributes(selectorArray) + '>' + selectorArray['Value'] + '</w:Selector>';
|
||
}
|
||
result += '</w:SelectorSet></a:ReferenceParameters></r:' + prop + '>';
|
||
}
|
||
else {
|
||
if (Array.isArray(putObj[prop])) {
|
||
for (var i = 0; i < putObj[prop].length; i++) {
|
||
result += '<r:' + prop + '>' + putObj[prop][i].toString() + '</r:' + prop + '>';
|
||
}
|
||
} else {
|
||
result += '<r:' + prop + '>' + putObj[prop].toString() + '</r:' + prop + '>';
|
||
}
|
||
}
|
||
}
|
||
|
||
result += '</r:' + objname + '>';
|
||
return result;
|
||
}
|
||
|
||
/*
|
||
convert
|
||
{ @Name: 'InstanceID', @AttrName: 'Attribute Value'}
|
||
into
|
||
' Name="InstanceID" AttrName="Attribute Value" '
|
||
*/
|
||
function _ObjectToXmlAttributes(objWithAttributes) {
|
||
if(!objWithAttributes) return '';
|
||
var result = ' ';
|
||
for (var propName in objWithAttributes) {
|
||
if (!objWithAttributes.hasOwnProperty(propName) || propName.indexOf('@') !== 0) continue;
|
||
result += propName.substring(1) + '="' + objWithAttributes[propName] + '" ';
|
||
}
|
||
return result;
|
||
}
|
||
|
||
function _PutObjToSelectorsXml(selectorSet) {
|
||
if (!selectorSet) return '';
|
||
if (typeof selectorSet == 'string') return selectorSet;
|
||
if (selectorSet['InstanceID']) return "<w:SelectorSet><w:Selector Name=\"InstanceID\">" + selectorSet['InstanceID'] + "</w:Selector></w:SelectorSet>";
|
||
var result = '<w:SelectorSet>';
|
||
for(var propName in selectorSet) {
|
||
if (!selectorSet.hasOwnProperty(propName)) continue;
|
||
result += '<w:Selector Name="' + propName + '">';
|
||
if (selectorSet[propName]['ReferenceParameters']) {
|
||
result += '<a:EndpointReference>';
|
||
result += '<a:Address>' + selectorSet[propName]['Address'] + '</a:Address><a:ReferenceParameters><w:ResourceURI>' + selectorSet[propName]['ReferenceParameters']['ResourceURI'] + '</w:ResourceURI><w:SelectorSet>';
|
||
var selectorArray = selectorSet[propName]['ReferenceParameters']['SelectorSet']['Selector'];
|
||
if (Array.isArray(selectorArray)) {
|
||
for (var i = 0; i < selectorArray.length; i++) {
|
||
result += '<w:Selector' + _ObjectToXmlAttributes(selectorArray[i]) + '>' + selectorArray[i]['Value'] + '</w:Selector>';
|
||
}
|
||
}
|
||
else {
|
||
result += '<w:Selector' + _ObjectToXmlAttributes(selectorArray) + '>' + selectorArray['Value'] + '</w:Selector>';
|
||
}
|
||
result += '</w:SelectorSet></a:ReferenceParameters></a:EndpointReference>';
|
||
} else {
|
||
result += selectorSet[propName];
|
||
}
|
||
result += '</w:Selector>';
|
||
}
|
||
result += '</w:SelectorSet>';
|
||
return result;
|
||
}
|
||
|
||
function _turnToXml(text) {
|
||
if (window.DOMParser) {
|
||
return new DOMParser().parseFromString(text, "text/xml");
|
||
}
|
||
else // Internet Explorer
|
||
{
|
||
var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
|
||
xmlDoc.async = false;
|
||
xmlDoc.loadXML(text);
|
||
return xmlDoc;
|
||
}
|
||
}
|
||
|
||
return obj;
|
||
}
|
||
/**
|
||
* @description Remote Desktop
|
||
* @author Ylian Saint-Hilaire
|
||
* @version v0.0.2g
|
||
*/
|
||
|
||
// Construct a MeshServer object
|
||
var CreateAmtRemoteDesktop = function (divid, scrolldiv) {
|
||
var obj = {};
|
||
obj.canvasid = divid;
|
||
obj.CanvasId = Q(divid);
|
||
obj.scrolldiv = scrolldiv;
|
||
obj.canvas = Q(divid).getContext("2d");
|
||
obj.protocol = 2; // KVM
|
||
obj.state = 0;
|
||
obj.acc = "";
|
||
obj.ScreenWidth = 960;
|
||
obj.ScreenHeight = 700;
|
||
obj.width = 0;
|
||
obj.height = 0;
|
||
obj.rwidth = 0;
|
||
obj.rheight = 0;
|
||
obj.bpp = 2; // Bytes per pixel (1 or 2 supported)
|
||
obj.useZRLE = true;
|
||
obj.showmouse = true;
|
||
obj.buttonmask = 0;
|
||
obj.localKeyMap = true;
|
||
//obj.inbytes = 0;
|
||
//obj.outbytes = 0;
|
||
obj.spare = null;
|
||
obj.sparew = 0;
|
||
obj.spareh = 0;
|
||
obj.sparew2 = 0;
|
||
obj.spareh2 = 0;
|
||
obj.sparecache = {};
|
||
obj.ZRLEfirst = 1;
|
||
obj.onScreenSizeChange = null;
|
||
obj.frameRateDelay = 0;
|
||
// ###BEGIN###{DesktopInband}
|
||
obj.kvmDataSupported = false;
|
||
obj.onKvmData = null;
|
||
obj.onKvmDataPending = [];
|
||
obj.onKvmDataAck = -1;
|
||
obj.holding = false;
|
||
obj.lastKeepAlive = Date.now();
|
||
// ###END###{DesktopInband}
|
||
|
||
|
||
// Private method
|
||
obj.Debug = function (msg) { console.log(msg); }
|
||
|
||
obj.xxStateChange = function (newstate) {
|
||
if (newstate == 0) {
|
||
obj.canvas.fillStyle = '#000000';
|
||
obj.canvas.fillRect(0, 0, obj.width, obj.height);
|
||
obj.canvas.canvas.width = obj.rwidth = obj.width = 640;
|
||
obj.canvas.canvas.height = obj.rheight = obj.height = 400;
|
||
QS(obj.canvasid).cursor = 'default';
|
||
} else {
|
||
QS(obj.canvasid).cursor = obj.showmouse ?'default':'none';
|
||
}
|
||
}
|
||
|
||
obj.ProcessData = function (data) {
|
||
if (!data) return;
|
||
// obj.Debug("KRecv(" + data.length + "): " + rstr2hex(data));
|
||
//obj.inbytes += data.length;
|
||
//obj.Debug("KRecv(" + obj.inbytes + ")");
|
||
obj.acc += data;
|
||
while (obj.acc.length > 0) {
|
||
//obj.Debug("KAcc(" + obj.acc.length + "): " + rstr2hex(obj.acc));
|
||
var cmdsize = 0;
|
||
if (obj.state == 0 && obj.acc.length >= 12) {
|
||
// Getting handshake & version
|
||
cmdsize = 12;
|
||
//if (obj.acc.substring(0, 4) != "RFB ") { return obj.Stop(); }
|
||
//var version = parseFloat(obj.acc.substring(4, 11));
|
||
//obj.Debug("KVersion: " + version);
|
||
obj.state = 1;
|
||
obj.send("RFB 003.008\n");
|
||
}
|
||
else if (obj.state == 1 && obj.acc.length >= 1) {
|
||
// Getting security options
|
||
cmdsize = obj.acc.charCodeAt(0) + 1;
|
||
obj.send(String.fromCharCode(1)); // Send the "None" security type. Since we already authenticated using redirection digest auth, we don't need to do this again.
|
||
obj.state = 2;
|
||
}
|
||
else if (obj.state == 2 && obj.acc.length >= 4) {
|
||
// Getting security response
|
||
cmdsize = 4;
|
||
if (ReadInt(obj.acc, 0) != 0) { return obj.Stop(); }
|
||
obj.send(String.fromCharCode(1)); // Send share desktop flag
|
||
obj.state = 3;
|
||
}
|
||
else if (obj.state == 3 && obj.acc.length >= 24) {
|
||
// Getting server init
|
||
var namelen = ReadInt(obj.acc, 20);
|
||
if (obj.acc.length < 24 + namelen) return;
|
||
cmdsize = 24 + namelen;
|
||
obj.canvas.canvas.width = obj.rwidth = obj.width = obj.ScreenWidth = ReadShort(obj.acc, 0);
|
||
obj.canvas.canvas.height = obj.rheight = obj.height = obj.ScreenHeight = ReadShort(obj.acc, 2);
|
||
|
||
// These are all values we don't really need, we are going to only run in RGB565 or RGB332 and not use the flexibility provided by these settings.
|
||
// Makes the javascript code smaller and maybe a bit faster.
|
||
/*
|
||
obj.xbpp = obj.acc.charCodeAt(4);
|
||
obj.depth = obj.acc.charCodeAt(5);
|
||
obj.bigend = obj.acc.charCodeAt(6);
|
||
obj.truecolor = obj.acc.charCodeAt(7);
|
||
obj.rmax = ReadShort(obj.acc, 8);
|
||
obj.gmax = ReadShort(obj.acc, 10);
|
||
obj.bmax = ReadShort(obj.acc, 12);
|
||
obj.rsh = obj.acc.charCodeAt(14);
|
||
obj.gsh = obj.acc.charCodeAt(15);
|
||
obj.bsh = obj.acc.charCodeAt(16);
|
||
var name = obj.acc.substring(24, 24 + namelen);
|
||
obj.Debug("name: " + name);
|
||
obj.Debug("width: " + obj.width + ", height: " + obj.height);
|
||
obj.Debug("bits-per-pixel: " + obj.xbpp);
|
||
obj.Debug("depth: " + obj.depth);
|
||
obj.Debug("big-endian-flag: " + obj.bigend);
|
||
obj.Debug("true-colour-flag: " + obj.truecolor);
|
||
obj.Debug("rgb max: " + obj.rmax + "," + obj.gmax + "," + obj.bmax);
|
||
obj.Debug("rgb shift: " + obj.rsh + "," + obj.gsh + "," + obj.bsh);
|
||
*/
|
||
|
||
// SetEncodings, with AMT we can't omit RAW, must be specified.
|
||
// Intel AMT supports encodings: RAW (0), ZRLE (16), Desktop Size (0xFFFFFF21, -223)
|
||
|
||
var supportedEncodings = '';
|
||
if (obj.useZRLE) supportedEncodings += IntToStr(16);
|
||
supportedEncodings += IntToStr(0);
|
||
// ###BEGIN###{DesktopInband}
|
||
supportedEncodings += IntToStr(1092);
|
||
// ###END###{DesktopInband}
|
||
|
||
obj.send(String.fromCharCode(2, 0) + ShortToStr((supportedEncodings.length / 4) + 1) + supportedEncodings + IntToStr(-223)); // Supported Encodings + Desktop Size
|
||
|
||
// Set the pixel encoding to something much smaller
|
||
// obj.send(String.fromCharCode(0, 0, 0, 0, 16, 16, 0, 1) + ShortToStr(31) + ShortToStr(63) + ShortToStr(31) + String.fromCharCode(11, 5, 0, 0, 0, 0)); // Setup 16 bit color RGB565 (This is the default, so we don't need to set it)
|
||
if (obj.bpp == 1) obj.send(String.fromCharCode(0, 0, 0, 0, 8, 8, 0, 1) + ShortToStr(7) + ShortToStr(7) + ShortToStr(3) + String.fromCharCode(5, 2, 0, 0, 0, 0)); // Setup 8 bit color RGB332
|
||
|
||
obj.state = 4;
|
||
obj.parent.xxStateChange(3);
|
||
_SendRefresh();
|
||
//obj.timer = setInterval(obj.xxOnTimer, 50);
|
||
|
||
|
||
if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight); }
|
||
}
|
||
else if (obj.state == 4) {
|
||
switch (obj.acc.charCodeAt(0)) {
|
||
case 0: // FramebufferUpdate
|
||
if (obj.acc.length < 4) return;
|
||
obj.state = 100 + ReadShort(obj.acc, 2); // Read the number of tiles that are going to be sent, add 100 and use that as our protocol state.
|
||
cmdsize = 4;
|
||
break;
|
||
case 2: // This is the bell, do nothing.
|
||
cmdsize = 1;
|
||
break;
|
||
case 3: // This is ServerCutText
|
||
if (obj.acc.length < 8) return;
|
||
var len = ReadInt(obj.acc, 4) + 8;
|
||
if (obj.acc.length < len) return;
|
||
cmdsize = handleServerCutText(obj.acc);
|
||
break;
|
||
}
|
||
}
|
||
else if (obj.state > 100 && obj.acc.length >= 12) {
|
||
var x = ReadShort(obj.acc, 0),
|
||
y = ReadShort(obj.acc, 2),
|
||
width = ReadShort(obj.acc, 4),
|
||
height = ReadShort(obj.acc, 6),
|
||
s = width * height,
|
||
encoding = ReadInt(obj.acc, 8);
|
||
|
||
if (encoding < 17) {
|
||
if (width < 1 || width > 64 || height < 1 || height > 64) { console.log("Invalid tile size (" + width + "," + height + "), disconnecting."); return obj.Stop(); }
|
||
|
||
// Set the spare bitmap to the rigth size if it's not already. This allows us to recycle the spare most if not all the time.
|
||
if (obj.sparew != width || obj.spareh != height) {
|
||
obj.sparew = obj.sparew2 = width;
|
||
obj.spareh = obj.spareh2 = height;
|
||
var xspacecachename = obj.sparew2 + 'x' + obj.spareh2;
|
||
obj.spare = obj.sparecache[xspacecachename];
|
||
if (!obj.spare) {
|
||
obj.sparecache[xspacecachename] = obj.spare = obj.canvas.createImageData(obj.sparew2, obj.spareh2);
|
||
var j = (obj.sparew2 * obj.spareh2) << 2;
|
||
for (var i = 3; i < j; i += 4) { obj.spare.data[i] = 0xFF; } // Set alpha channel to opaque.
|
||
}
|
||
}
|
||
}
|
||
|
||
if (encoding == 0xFFFFFF21) {
|
||
// Desktop Size (0xFFFFFF21, -223)
|
||
obj.canvas.canvas.width = obj.rwidth = obj.width = width;
|
||
obj.canvas.canvas.height = obj.rheight = obj.height = height;
|
||
obj.send(String.fromCharCode(3, 0, 0, 0, 0, 0) + ShortToStr(obj.width) + ShortToStr(obj.height)); // FramebufferUpdateRequest
|
||
cmdsize = 12;
|
||
if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight); }
|
||
// obj.Debug("New desktop width: " + obj.width + ", height: " + obj.height);
|
||
}
|
||
else if (encoding == 0) {
|
||
// RAW encoding
|
||
var ptr = 12, cs = 12 + (s * obj.bpp);
|
||
if (obj.acc.length < cs) return; // Check we have all the data needed and we can only draw 64x64 tiles.
|
||
cmdsize = cs;
|
||
|
||
// CRITICAL LOOP, optimize this as much as possible
|
||
if (obj.bpp == 2) {
|
||
for (var i = 0; i < s; i++) { _setPixel16(obj.acc.charCodeAt(ptr++) + (obj.acc.charCodeAt(ptr++) << 8), i); }
|
||
} else {
|
||
for (var i = 0; i < s; i++) { _setPixel8(obj.acc.charCodeAt(ptr++), i); }
|
||
}
|
||
_putImage(obj.spare, x, y);
|
||
}
|
||
else if (encoding == 16) {
|
||
// ZRLE encoding
|
||
if (obj.acc.length < 16) return;
|
||
var datalen = ReadInt(obj.acc, 12);
|
||
if (obj.acc.length < (16 + datalen)) return;
|
||
//obj.Debug("RECT ZRLE (" + x + "," + y + "," + width + "," + height + ") LEN = " + datalen);
|
||
//obj.Debug("RECT ZRLE LEN: " + ReadShortX(obj.acc, 17) + ", DATA: " + rstr2hex(obj.acc.substring(16)));
|
||
|
||
// Process the ZLib header if this is the first block
|
||
var ptr = 16, delta = 5, dx = 0;
|
||
|
||
if (datalen > 5 && obj.acc.charCodeAt(ptr) == 0 && ReadShortX(obj.acc, ptr + 1) == (datalen - delta)) {
|
||
// This is an uncompressed ZLib data block
|
||
_decodeLRE(obj.acc, ptr + 5, x, y, width, height, s, datalen);
|
||
}
|
||
|
||
cmdsize = 16 + datalen;
|
||
}
|
||
else {
|
||
obj.Debug("Unknown Encoding: " + encoding);
|
||
return obj.Stop();
|
||
}
|
||
if (--obj.state == 100) {
|
||
obj.state = 4;
|
||
if (obj.frameRateDelay == 0) {
|
||
_SendRefresh(); // Ask for new frame
|
||
} else {
|
||
setTimeout(_SendRefresh, obj.frameRateDelay); // Hold x miliseconds before asking for a new frame
|
||
}
|
||
}
|
||
}
|
||
|
||
if (cmdsize == 0) return;
|
||
obj.acc = obj.acc.substring(cmdsize);
|
||
}
|
||
}
|
||
|
||
function _decodeLRE(data, ptr, x, y, width, height, s, datalen) {
|
||
var subencoding = data.charCodeAt(ptr++), index, v, runlengthdecode, palette = {}, rlecount = 0, runlength = 0, i;
|
||
// obj.Debug("RECT RLE (" + (datalen - 5) + ", " + subencoding + "):" + rstr2hex(data.substring(21, 21 + (datalen - 5))));
|
||
if (subencoding == 0) {
|
||
// RAW encoding
|
||
if (obj.bpp == 2) {
|
||
for (i = 0; i < s; i++) { _setPixel16(data.charCodeAt(ptr++) + (data.charCodeAt(ptr++) << 8), i); }
|
||
} else {
|
||
for (i = 0; i < s; i++) { _setPixel8(data.charCodeAt(ptr++), i); }
|
||
}
|
||
_putImage(obj.spare, x, y);
|
||
}
|
||
else if (subencoding == 1) {
|
||
// Solid color tile
|
||
v = data.charCodeAt(ptr++) + ((obj.bpp == 2) ? (data.charCodeAt(ptr++) << 8) : 0);
|
||
obj.canvas.fillStyle = 'rgb(' + ((obj.bpp == 1) ? ((v & 224) + ',' + ((v & 28) << 3) + ',' + _fixColor((v & 3) << 6)) : (((v >> 8) & 248) + ',' + ((v >> 3) & 252) + ',' + ((v & 31) << 3))) + ')';
|
||
|
||
|
||
obj.canvas.fillRect(x, y, width, height);
|
||
}
|
||
else if (subencoding > 1 && subencoding < 17) { // Packed palette encoded tile
|
||
// Read the palette
|
||
var br = 4, bm = 15; // br is BitRead and bm is BitMask. By adjusting these two we can support all the variations in this encoding.
|
||
if (obj.bpp == 2) {
|
||
for (i = 0; i < subencoding; i++) { palette[i] = data.charCodeAt(ptr++) + (data.charCodeAt(ptr++) << 8); }
|
||
if (subencoding == 2) { br = 1; bm = 1; } else if (subencoding <= 4) { br = 2; bm = 3; } // Compute bits to read & bit mark
|
||
while (rlecount < s && ptr < data.length) { v = data.charCodeAt(ptr++); for (i = (8 - br) ; i >= 0; i -= br) { _setPixel16(palette[(v >> i) & bm], rlecount++); } } // Display all the bits
|
||
} else {
|
||
for (i = 0; i < subencoding; i++) { palette[i] = data.charCodeAt(ptr++); }
|
||
if (subencoding == 2) { br = 1; bm = 1; } else if (subencoding <= 4) { br = 2; bm = 3; } // Compute bits to read & bit mark
|
||
while (rlecount < s && ptr < data.length) { v = data.charCodeAt(ptr++); for (i = (8 - br) ; i >= 0; i -= br) { _setPixel8(palette[(v >> i) & bm], rlecount++); } } // Display all the bits
|
||
}
|
||
_putImage(obj.spare, x, y);
|
||
}
|
||
else if (subencoding == 128) { // RLE encoded tile
|
||
if (obj.bpp == 2) {
|
||
while (rlecount < s && ptr < data.length) {
|
||
// Get the run color
|
||
v = data.charCodeAt(ptr++) + (data.charCodeAt(ptr++) << 8);
|
||
|
||
// Decode the run length. This is the fastest and most compact way I found to do this.
|
||
runlength = 1; do { runlength += (runlengthdecode = data.charCodeAt(ptr++)); } while (runlengthdecode == 255);
|
||
|
||
// Draw a run
|
||
if (obj.rotation == 0) {
|
||
_setPixel16run(v, rlecount, runlength); rlecount += runlength;
|
||
} else {
|
||
while (--runlength >= 0) { _setPixel16(v, rlecount++); }
|
||
}
|
||
}
|
||
} else {
|
||
while (rlecount < s && ptr < data.length) {
|
||
// Get the run color
|
||
v = data.charCodeAt(ptr++);
|
||
|
||
// Decode the run length. This is the fastest and most compact way I found to do this.
|
||
runlength = 1; do { runlength += (runlengthdecode = data.charCodeAt(ptr++)); } while (runlengthdecode == 255);
|
||
|
||
// Draw a run
|
||
if (obj.rotation == 0) {
|
||
_setPixel8run(v, rlecount, runlength); rlecount += runlength;
|
||
} else {
|
||
while (--runlength >= 0) { _setPixel8(v, rlecount++); }
|
||
}
|
||
}
|
||
}
|
||
_putImage(obj.spare, x, y);
|
||
}
|
||
else if (subencoding > 129) { // Palette RLE encoded tile
|
||
// Read the palette
|
||
if (obj.bpp == 2) {
|
||
for (i = 0; i < (subencoding - 128) ; i++) { palette[i] = data.charCodeAt(ptr++) + (data.charCodeAt(ptr++) << 8); }
|
||
} else {
|
||
for (i = 0; i < (subencoding - 128) ; i++) { palette[i] = data.charCodeAt(ptr++); }
|
||
}
|
||
|
||
// Decode RLE on palette
|
||
while (rlecount < s && ptr < data.length) {
|
||
// Setup the run, get the color index and get the color from the palette.
|
||
runlength = 1; index = data.charCodeAt(ptr++); v = palette[index % 128];
|
||
|
||
// If the index starts with high order bit 1, this is a run and decode the run length.
|
||
if (index > 127) { do { runlength += (runlengthdecode = data.charCodeAt(ptr++)); } while (runlengthdecode == 255); }
|
||
|
||
// Draw a run
|
||
if (obj.rotation == 0) {
|
||
if (obj.bpp == 2) {
|
||
_setPixel16run(v, rlecount, runlength); rlecount += runlength;
|
||
} else {
|
||
_setPixel8run(v, rlecount, runlength); rlecount += runlength;
|
||
}
|
||
} else {
|
||
if (obj.bpp == 2) {
|
||
while (--runlength >= 0) { _setPixel16(v, rlecount++); }
|
||
} else {
|
||
while (--runlength >= 0) { _setPixel8(v, rlecount++); }
|
||
}
|
||
}
|
||
}
|
||
_putImage(obj.spare, x, y);
|
||
}
|
||
}
|
||
|
||
// ###BEGIN###{DesktopInband}
|
||
obj.hold = function (holding) {
|
||
if (obj.holding == holding) return;
|
||
obj.holding = holding;
|
||
obj.canvas.fillStyle = '#000000';
|
||
obj.canvas.fillRect(0, 0, obj.width, obj.height); // Paint black
|
||
if (obj.holding == false) {
|
||
// Go back to normal operations
|
||
// Set canvas size and ask for full screen refresh
|
||
if ((obj.canvas.canvas.width != obj.width) || (obj.canvas.canvas.height != obj.height)) {
|
||
obj.canvas.canvas.width = obj.width; obj.canvas.canvas.height = obj.height;
|
||
if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight); } // ???
|
||
}
|
||
obj.Send(String.fromCharCode(3, 0, 0, 0, 0, 0) + ShortToStr(obj.width) + ShortToStr(obj.height)); // FramebufferUpdateRequest
|
||
} else {
|
||
obj.UnGrabMouseInput();
|
||
obj.UnGrabKeyInput();
|
||
}
|
||
}
|
||
// ###END###{DesktopInband}
|
||
|
||
function _putImage(i, x, y) {
|
||
// ###BEGIN###{DesktopInband}
|
||
if (obj.holding == true) return;
|
||
// ###END###{DesktopInband}
|
||
obj.canvas.putImageData(i, x, y);
|
||
}
|
||
|
||
// Set 8bit color RGB332
|
||
function _setPixel8(v, p) {
|
||
var pp = p << 2;
|
||
|
||
|
||
obj.spare.data[pp] = v & 224;
|
||
obj.spare.data[pp + 1] = (v & 28) << 3;
|
||
obj.spare.data[pp + 2] = _fixColor((v & 3) << 6);
|
||
}
|
||
|
||
// Set 16bit color RGB565
|
||
function _setPixel16(v, p) {
|
||
var pp = p << 2;
|
||
|
||
|
||
obj.spare.data[pp] = (v >> 8) & 248;
|
||
obj.spare.data[pp + 1] = (v >> 3) & 252;
|
||
obj.spare.data[pp + 2] = (v & 31) << 3;
|
||
}
|
||
|
||
// Set a run of 8bit color RGB332
|
||
function _setPixel8run(v, p, run) {
|
||
var pp = (p << 2), r = (v & 224), g = ((v & 28) << 3), b = (_fixColor((v & 3) << 6));
|
||
while (--run >= 0) { obj.spare.data[pp] = r; obj.spare.data[pp + 1] = g; obj.spare.data[pp + 2] = b; pp += 4; }
|
||
}
|
||
|
||
// Set a run of 16bit color RGB565
|
||
function _setPixel16run(v, p, run) {
|
||
var pp = (p << 2), r = ((v >> 8) & 248), g = ((v >> 3) & 252), b = ((v & 31) << 3);
|
||
while (--run >= 0) { obj.spare.data[pp] = r; obj.spare.data[pp + 1] = g; obj.spare.data[pp + 2] = b; pp += 4; }
|
||
}
|
||
|
||
|
||
function _fixColor(c) { return (c > 127) ? (c + 32) : c; }
|
||
|
||
function _SendRefresh() {
|
||
// ###BEGIN###{DesktopInband}
|
||
if (obj.holding == true) return;
|
||
// ###END###{DesktopInband}
|
||
// Request the entire screen
|
||
obj.send(String.fromCharCode(3, 1, 0, 0, 0, 0) + ShortToStr(obj.rwidth) + ShortToStr(obj.rheight)); // FramebufferUpdateRequest
|
||
}
|
||
|
||
obj.Start = function () {
|
||
//obj.Debug("KVM-Start");
|
||
obj.state = 0;
|
||
obj.acc = "";
|
||
obj.ZRLEfirst = 1;
|
||
//obj.inbytes = 0;
|
||
//obj.outbytes = 0;
|
||
// ###BEGIN###{DesktopInband}
|
||
obj.onKvmDataPending = [];
|
||
obj.onKvmDataAck = -1;
|
||
obj.kvmDataSupported = false;
|
||
// ###END###{DesktopInband}
|
||
for (var i in obj.sparecache) { delete obj.sparecache[i]; }
|
||
}
|
||
|
||
obj.Stop = function () {
|
||
obj.UnGrabMouseInput();
|
||
obj.UnGrabKeyInput();
|
||
obj.parent.Stop();
|
||
}
|
||
|
||
obj.send = function (x) {
|
||
//obj.Debug("KSend(" + x.length + "): " + rstr2hex(x));
|
||
//obj.outbytes += x.length;
|
||
obj.parent.send(x);
|
||
}
|
||
|
||
var convertAmtKeyCodeTable = {
|
||
"Pause": 19,
|
||
"CapsLock": 20,
|
||
"Space": 32,
|
||
"Quote": 39,
|
||
"Minus": 45,
|
||
"NumpadMultiply": 42,
|
||
"NumpadAdd": 43,
|
||
"PrintScreen": 44,
|
||
"Comma": 44,
|
||
"NumpadSubtract": 45,
|
||
"NumpadDecimal": 46,
|
||
"Period": 46,
|
||
"Slash": 47,
|
||
"NumpadDivide": 47,
|
||
"Semicolon": 59,
|
||
"Equal": 61,
|
||
"OSLeft": 91,
|
||
"BracketLeft": 91,
|
||
"OSRight": 91,
|
||
"Backslash": 92,
|
||
"BracketRight": 93,
|
||
"ContextMenu": 93,
|
||
"Backquote": 96,
|
||
"NumLock": 144,
|
||
"ScrollLock": 145,
|
||
"Backspace": 0xff08,
|
||
"Tab": 0xff09,
|
||
"Enter": 0xff0d,
|
||
"NumpadEnter": 0xff0d,
|
||
"Escape": 0xff1b,
|
||
"Delete": 0xffff,
|
||
"Home": 0xff50,
|
||
"PageUp": 0xff55,
|
||
"PageDown": 0xff56,
|
||
"ArrowLeft": 0xff51,
|
||
"ArrowUp": 0xff52,
|
||
"ArrowRight": 0xff53,
|
||
"ArrowDown": 0xff54,
|
||
"End": 0xff57,
|
||
"Insert": 0xff63,
|
||
"F1": 0xffbe,
|
||
"F2": 0xffbf,
|
||
"F3": 0xffc0,
|
||
"F4": 0xffc1,
|
||
"F5": 0xffc2,
|
||
"F6": 0xffc3,
|
||
"F7": 0xffc4,
|
||
"F8": 0xffc5,
|
||
"F9": 0xffc6,
|
||
"F10": 0xffc7,
|
||
"F11": 0xffc8,
|
||
"F12": 0xffc9,
|
||
"ShiftLeft": 0xffe1,
|
||
"ShiftRight": 0xffe2,
|
||
"ControlLeft": 0xffe3,
|
||
"ControlRight": 0xffe4,
|
||
"AltLeft": 0xffe9,
|
||
"AltRight": 0xffea,
|
||
"MetaLeft": 0xffe7,
|
||
"MetaRight": 0xffe8
|
||
}
|
||
function convertAmtKeyCode(e) {
|
||
if (e.code.startsWith('Key') && e.code.length == 4) { return e.code.charCodeAt(3) + ((e.shiftKey == false) ? 32 : 0); }
|
||
if (e.code.startsWith('Digit') && e.code.length == 6) { return e.code.charCodeAt(5); }
|
||
if (e.code.startsWith('Numpad') && e.code.length == 7) { return e.code.charCodeAt(6); }
|
||
return convertAmtKeyCodeTable[e.code];
|
||
}
|
||
|
||
/*
|
||
Intel AMT only recognizes a small subset of keysym characters defined in the keysymdef.h so you don<6F>t need to
|
||
implement all the languages (this is taken care by the USB Scancode Extension in RFB4.0 protocol).
|
||
The only subset recognized by the FW is the defined by the following sets : XK_LATIN1 , XK_MISCELLANY, XK_3270, XK_XKB_KEYS, XK_KATAKANA.
|
||
In addition to keysymdef.h symbols there are 6 japanese extra keys that we do support:
|
||
|
||
#define XK_Intel_EU_102kbd_backslash_pipe_45 0x17170056 // European 102-key: 45 (backslash/pipe), usb Usage: 0x64
|
||
#define XK_Intel_JP_106kbd_yen_pipe 0x1717007d // Japanese 106-key: 14 (Yen/pipe), usb Usage: 0x89
|
||
#define XK_Intel_JP_106kbd_backslash_underbar 0x17170073 // Japanese 106-key: 56 (backslash/underbar), usb Usage: 0x87
|
||
#define XK_Intel_JP_106kbd_NoConvert 0x1717007b // Japanese 106-key: 131 (NoConvert), usb Usage: 0x8b
|
||
#define XK_Intel_JP_106kbd_Convert 0x17170079 // Japanese 106-key: 132 (Convert), usb Usage: 0x8a
|
||
#define XK_Intel_JP_106kbd_Hirigana_Katakana 0x17170070 // Japanese 106-key: 133 (Hirigana/Katakana), usb Usage: 0x88
|
||
*/
|
||
|
||
function _keyevent(d, e) {
|
||
if (!e) { e = window.event; }
|
||
|
||
if (e.code && (obj.localKeyMap == false)) {
|
||
// For new browsers, this mapping is keyboard language independent
|
||
var k = convertAmtKeyCode(e);
|
||
if (k != null) { obj.sendkey(k, d); }
|
||
} else {
|
||
// For older browsers, this mapping works best for EN-US keyboard
|
||
var k = e.keyCode, kk = k;
|
||
if (e.shiftKey == false && k >= 65 && k <= 90) kk = k + 32;
|
||
if (k >= 112 && k <= 124) kk = k + 0xFF4E;
|
||
if (k == 8) kk = 0xff08; // Backspace
|
||
if (k == 9) kk = 0xff09; // Tab
|
||
if (k == 13) kk = 0xff0d; // Return
|
||
if (k == 16) kk = 0xffe1; // Shift (Left)
|
||
if (k == 17) kk = 0xffe3; // Ctrl (Left)
|
||
if (k == 18) kk = 0xffe9; // Alt (Left)
|
||
if (k == 27) kk = 0xff1b; // ESC
|
||
if (k == 33) kk = 0xff55; // PageUp
|
||
if (k == 34) kk = 0xff56; // PageDown
|
||
if (k == 35) kk = 0xff57; // End
|
||
if (k == 36) kk = 0xff50; // Home
|
||
if (k == 37) kk = 0xff51; // Left
|
||
if (k == 38) kk = 0xff52; // Up
|
||
if (k == 39) kk = 0xff53; // Right
|
||
if (k == 40) kk = 0xff54; // Down
|
||
if (k == 45) kk = 0xff63; // Insert
|
||
if (k == 46) kk = 0xffff; // Delete
|
||
if (k >= 96 && k <= 105) kk = k - 48; // Key pad numbers
|
||
if (k == 106) kk = 42; // Pad *
|
||
if (k == 107) kk = 43; // Pad +
|
||
if (k == 109) kk = 45; // Pad -
|
||
if (k == 110) kk = 46; // Pad .
|
||
if (k == 111) kk = 47; // Pad /
|
||
if (k == 186) kk = 59; // ;
|
||
if (k == 187) kk = 61; // =
|
||
if (k == 188) kk = 44; // ,
|
||
if (k == 189) kk = 45; // -
|
||
if (k == 190) kk = 46; // .
|
||
if (k == 191) kk = 47; // /
|
||
if (k == 192) kk = 96; // `
|
||
if (k == 219) kk = 91; // [
|
||
if (k == 220) kk = 92; // \
|
||
if (k == 221) kk = 93; // ]
|
||
if (k == 222) kk = 39; // '
|
||
//console.log('Key' + d + ": " + k + " = " + kk);
|
||
obj.sendkey(kk, d);
|
||
}
|
||
return obj.haltEvent(e);
|
||
}
|
||
|
||
obj.sendkey = function (k, d) {
|
||
if (typeof k == 'object') { for (var i in k) { obj.sendkey(k[i][0], k[i][1]); } }
|
||
else { obj.send(String.fromCharCode(4, d, 0, 0) + IntToStr(k)); }
|
||
}
|
||
|
||
function handleServerCutText(acc) {
|
||
if (acc.length < 8) return 0;
|
||
var len = ReadInt(obj.acc, 4) + 8;
|
||
if (acc.length < len) return 0;
|
||
// ###BEGIN###{DesktopInband}
|
||
if (obj.onKvmData != null) {
|
||
var d = acc.substring(8, len);
|
||
if ((d.length >= 16) && (d.substring(0, 15) == '\0KvmDataChannel')) {
|
||
if (obj.kvmDataSupported == false) { obj.kvmDataSupported = true; console.log('KVM Data Channel Supported.'); }
|
||
if (((obj.onKvmDataAck == -1) && (d.length == 16)) || (d.charCodeAt(15) != 0)) { obj.onKvmDataAck = true; }
|
||
//if (urlvars && urlvars['kvmdatatrace']) { console.log('KVM-Recv(' + (d.length - 16) + '): ' + d.substring(16)); }
|
||
if (d.length >= 16) { obj.onKvmData(d.substring(16)); } // Event the data and ack
|
||
if ((obj.onKvmDataAck == true) && (obj.onKvmDataPending.length > 0)) { obj.sendKvmData(obj.onKvmDataPending.shift()); } // Send pending data
|
||
}
|
||
}
|
||
// ###END###{DesktopInband}
|
||
return len;
|
||
}
|
||
|
||
// ###BEGIN###{DesktopInband}
|
||
obj.sendKvmData = function (x) {
|
||
if (obj.onKvmDataAck !== true) {
|
||
obj.onKvmDataPending.push(x);
|
||
} else {
|
||
//if (urlvars && urlvars['kvmdatatrace']) { console.log('KVM-Send(' + x.length + '): ' + x); }
|
||
x = '\0KvmDataChannel\0' + x;
|
||
obj.send(String.fromCharCode(6, 0, 0, 0) + IntToStr(x.length) + x);
|
||
obj.onKvmDataAck = false;
|
||
}
|
||
}
|
||
|
||
// Send a HWKVM keep alive if it's not been sent in the last 5 seconds.
|
||
obj.sendKeepAlive = function () {
|
||
if (obj.lastKeepAlive < Date.now() - 5000) { obj.lastKeepAlive = Date.now(); obj.send(String.fromCharCode(6, 0, 0, 0) + IntToStr(16) + '\0KvmDataChannel\0'); }
|
||
}
|
||
// ###END###{DesktopInband}
|
||
|
||
obj.SendCtrlAltDelMsg = function () { obj.sendcad(); }
|
||
obj.sendcad = function () { obj.sendkey([[0xFFE3, 1], [0xFFE9, 1], [0xFFFF, 1], [0xFFFF, 0], [0xFFE9, 0], [0xFFE3, 0]]); } // Control down, Alt down, Delete down, Delete up , Alt up , Control up
|
||
|
||
var _MouseInputGrab = false;
|
||
var _KeyInputGrab = false;
|
||
|
||
obj.GrabMouseInput = function () {
|
||
if (_MouseInputGrab == true) return;
|
||
var c = obj.canvas.canvas;
|
||
c.onmouseup = obj.mouseup;
|
||
c.onmousedown = obj.mousedown;
|
||
c.onmousemove = obj.mousemove;
|
||
//if (navigator.userAgent.match(/mozilla/i)) c.DOMMouseScroll = obj.xxDOMMouseScroll; else c.onmousewheel = obj.xxMouseWheel;
|
||
_MouseInputGrab = true;
|
||
}
|
||
|
||
obj.UnGrabMouseInput = function () {
|
||
if (_MouseInputGrab == false) return;
|
||
var c = obj.canvas.canvas;
|
||
c.onmousemove = null;
|
||
c.onmouseup = null;
|
||
c.onmousedown = null;
|
||
//if (navigator.userAgent.match(/mozilla/i)) c.DOMMouseScroll = null; else c.onmousewheel = null;
|
||
_MouseInputGrab = false;
|
||
}
|
||
|
||
obj.GrabKeyInput = function () {
|
||
if (_KeyInputGrab == true) return;
|
||
document.onkeyup = obj.handleKeyUp;
|
||
document.onkeydown = obj.handleKeyDown;
|
||
document.onkeypress = obj.handleKeys;
|
||
_KeyInputGrab = true;
|
||
}
|
||
|
||
obj.UnGrabKeyInput = function () {
|
||
if (_KeyInputGrab == false) return;
|
||
document.onkeyup = null;
|
||
document.onkeydown = null;
|
||
document.onkeypress = null;
|
||
_KeyInputGrab = false;
|
||
}
|
||
|
||
obj.handleKeys = function (e) { return obj.haltEvent(e); }
|
||
obj.handleKeyUp = function (e) { return _keyevent(0, e); }
|
||
obj.handleKeyDown = function (e) { return _keyevent(1, e); }
|
||
obj.haltEvent = function (e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
|
||
|
||
// RFB "PointerEvent" and mouse handlers
|
||
obj.mousedblclick = function (e) { }
|
||
obj.mousedown = function (e) { obj.buttonmask |= (1 << e.button); return obj.mousemove(e); }
|
||
obj.mouseup = function (e) { obj.buttonmask &= (0xFFFF - (1 << e.button)); return obj.mousemove(e); }
|
||
obj.mousemove = function (e) {
|
||
if (obj.state != 4) return true;
|
||
var ScaleFactorHeight = (obj.canvas.canvas.height / Q(obj.canvasid).offsetHeight);
|
||
var ScaleFactorWidth = (obj.canvas.canvas.width / Q(obj.canvasid).offsetWidth);
|
||
var Offsets = obj.getPositionOfControl(Q(obj.canvasid));
|
||
obj.mx = ((event.pageX - Offsets[0]) * ScaleFactorWidth);
|
||
obj.my = ((event.pageY - Offsets[1]) * ScaleFactorHeight);
|
||
if (event.addx) { obj.mx += event.addx; }
|
||
if (event.addy) { obj.my += event.addy; }
|
||
|
||
|
||
obj.send(String.fromCharCode(5, obj.buttonmask) + ShortToStr(obj.mx) + ShortToStr(obj.my));
|
||
|
||
|
||
return obj.haltEvent(e);
|
||
}
|
||
|
||
obj.getPositionOfControl = function (Control) {
|
||
var Position = Array(2);
|
||
Position[0] = Position[1] = 0;
|
||
while (Control) {
|
||
Position[0] += Control.offsetLeft;
|
||
Position[1] += Control.offsetTop;
|
||
Control = Control.offsetParent;
|
||
}
|
||
return Position;
|
||
}
|
||
|
||
return obj;
|
||
}
|
||
/**
|
||
* @description Remote Terminal
|
||
* @author Ylian Saint-Hilaire
|
||
* @version v0.0.2c
|
||
*/
|
||
|
||
// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
|
||
// https://www.x.org/docs/xterm/ctlseqs.pdf
|
||
|
||
// Construct a MeshServer object
|
||
var CreateAmtRemoteTerminal = function (divid, options) {
|
||
var obj = {};
|
||
obj.DivId = divid;
|
||
obj.DivElement = document.getElementById(divid);
|
||
obj.protocol = 1; // SOL
|
||
obj.fxEmulation = 0;
|
||
obj.lineFeed = '\r\n';
|
||
obj.debugmode = 0;
|
||
|
||
obj.width = 80; // 80 or 100
|
||
obj.height = 25; // 25 or 30
|
||
obj.heightLock = 0;
|
||
|
||
var _Terminal_CellHeight = 21;
|
||
var _Terminal_CellWidth = 13;
|
||
var _TermColors = ['000000', 'BB0000', '00BB00', 'BBBB00', '0000BB', 'BB00BB', '00BBBB', 'BBBBBB', '555555', 'FF5555', '55FF55', 'FFFF55', '5555FF', 'FF55FF', '55FFFF', 'FFFFFF'];
|
||
var _TermCurrentReverse = 0;
|
||
var _TermCurrentFColor = 7;
|
||
var _TermCurrentBColor = 0;
|
||
var _TermLineWrap = true;
|
||
var _termx = 0;
|
||
var _termy = 0;
|
||
var _termsavex = 0;
|
||
var _termsavey = 0;
|
||
var _termstate = 0;
|
||
var _escNumber = [];
|
||
var _escNumberPtr = 0;
|
||
var _escNumberMode = 0;
|
||
var _scratt = [];
|
||
var _tscreen = [];
|
||
var _VTUNDERLINE = 1;
|
||
var _VTREVERSE = 2;
|
||
var _backSpaceErase = false;
|
||
var _cursorVisible = true;
|
||
var _scrollRegion;
|
||
var _altKeypadMode = false;
|
||
var scrollBackBuffer = [];
|
||
obj.title = null;
|
||
obj.onTitleChange = null;
|
||
|
||
obj.Start = function () { }
|
||
|
||
obj.Init = function (width, height) {
|
||
obj.width = width ? width : 80;
|
||
obj.height = height ? height : 25;
|
||
for (var y = 0; y < obj.height; y++) {
|
||
_tscreen[y] = [];
|
||
_scratt[y] = [];
|
||
for (var x = 0; x < obj.width; x++) { _tscreen[y][x] = ' '; _scratt[y][x] = (7 << 6); }
|
||
}
|
||
obj.TermInit();
|
||
obj.TermDraw();
|
||
}
|
||
|
||
obj.xxStateChange = function (newstate) {
|
||
if ((newstate == 3) && (options != null) && (options.xterm == true)) { obj.TermSendKeys('stty rows ' + obj.height + ' cols ' + obj.width + '\nclear\n'); }
|
||
}
|
||
|
||
obj.ProcessData = function (str) {
|
||
if (obj.debugmode == 2) { console.log("TRecv(" + str.length + "): " + rstr2hex(str)); }
|
||
if (obj.capture != null) obj.capture += str; _ProcessVt100EscString(str); obj.TermDraw();
|
||
}
|
||
|
||
function _ProcessVt100EscString(str) { for (var i = 0; i < str.length; i++) _ProcessVt100EscChar(String.fromCharCode(str.charCodeAt(i)), str.charCodeAt(i)); }
|
||
|
||
function _ProcessVt100EscChar(b, c) {
|
||
switch (_termstate) {
|
||
case 0: // Normal Term State
|
||
switch (c) {
|
||
case 27: // ESC
|
||
_termstate = 1;
|
||
_escNumber = [];
|
||
_escNumberPtr = 0;
|
||
_escNumberMode = 0;
|
||
break;
|
||
default:
|
||
// Process a single char
|
||
_ProcessVt100Char(b);
|
||
break;
|
||
}
|
||
break;
|
||
case 1:
|
||
switch (b) {
|
||
case '[':
|
||
_termstate = 2;
|
||
break;
|
||
case '(':
|
||
_termstate = 4;
|
||
break;
|
||
case ')':
|
||
_termstate = 5;
|
||
break;
|
||
case ']':
|
||
_termstate = 6; // xterm strings
|
||
break;
|
||
case '=':
|
||
// Set alternate keypad mode
|
||
_altKeypadMode = true;
|
||
_termstate = 0;
|
||
break;
|
||
case '>':
|
||
// Set numeric keypad mode
|
||
_altKeypadMode = false;
|
||
_termstate = 0;
|
||
break;
|
||
case '7':
|
||
// Save Cursor
|
||
_termsavex = _termx;
|
||
_termsavey = _termy;
|
||
_termstate = 0;
|
||
break;
|
||
case '8':
|
||
// Restore Cursor
|
||
_termx = _termsavex;
|
||
_termy = _termsavey;
|
||
_termstate = 0;
|
||
break;
|
||
case 'M':
|
||
// Scroll down one
|
||
var x = 1;
|
||
for (var y = _scrollRegion[1]; y >= _scrollRegion[0] + x; y--) {
|
||
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y - x][z]; _scratt[y][z] = _scratt[y - x][z]; }
|
||
}
|
||
for (var y = _scrollRegion[0] + x - 1; y > _scrollRegion[0] - 1; y--) {
|
||
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
|
||
}
|
||
_termstate = 0;
|
||
break;
|
||
default:
|
||
console.log('unknown terminal short code', b);
|
||
_termstate = 0;
|
||
break;
|
||
}
|
||
break;
|
||
case 2:
|
||
if (b >= '0' && b <= '9') {
|
||
// This is a number
|
||
if (!_escNumber[_escNumberPtr]) { _escNumber[_escNumberPtr] = (b - '0'); }
|
||
else { _escNumber[_escNumberPtr] = ((_escNumber[_escNumberPtr] * 10) + (b - '0')); }
|
||
break;
|
||
} else if (b == ';') {
|
||
// New number
|
||
_escNumberPtr++;
|
||
break;
|
||
} else if (b == '?') {
|
||
_escNumberMode = 1;
|
||
break;
|
||
} else {
|
||
// Process Escape Sequence
|
||
if (!_escNumber[0]) _escNumber[0] = 0;
|
||
_ProcessEscapeHandler(b, _escNumber, _escNumberPtr + 1, _escNumberMode);
|
||
_termstate = 0;
|
||
}
|
||
break;
|
||
case 4: // '(' Code
|
||
_termstate = 0;
|
||
break;
|
||
case 5: // ')' Code
|
||
_termstate = 0;
|
||
break;
|
||
case 6: // ']' Code, xterm
|
||
const bx = b.charCodeAt(0);
|
||
if (b == ';') {
|
||
_escNumberPtr++;
|
||
} else if (bx == 7) {
|
||
_ProcessXTermHandler(_escNumber);
|
||
_termstate = 0;
|
||
} else {
|
||
if (!_escNumber[_escNumberPtr]) { _escNumber[_escNumberPtr] = b; }
|
||
else { _escNumber[_escNumberPtr] += b; }
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
function _ProcessXTermHandler(_escNumber) {
|
||
if (_escNumber.length == 0) return;
|
||
var cmd = parseInt(_escNumber[0]);
|
||
if ((cmd == 0 || cmd == 2) && (_escNumber.length > 1) && (_escNumber[1] != '?')) {
|
||
if (obj.onTitleChange) { obj.onTitleChange(obj, obj.title = _escNumber[1]); }
|
||
}
|
||
}
|
||
|
||
function _ProcessEscapeHandler(code, args, argslen, mode) {
|
||
//console.log('process', code, args, mode);
|
||
if (mode == 1) {
|
||
switch (code) {
|
||
case 'l': // Hide the cursor
|
||
if (args[0] == 25) { _cursorVisible = false; }
|
||
break;
|
||
case 'h': // Show the cursor
|
||
if (args[0] == 25) { _cursorVisible = true; }
|
||
break;
|
||
}
|
||
} else if (mode == 0) {
|
||
var i;
|
||
switch (code) {
|
||
case 'c': // ResetDevice
|
||
// Reset
|
||
obj.TermResetScreen();
|
||
break;
|
||
case 'A': // Move cursor up n lines
|
||
if (argslen == 1) {
|
||
if (args[0] == 0) { _termy--; } else { _termy -= args[0]; }
|
||
if (_termy < 0) _termy = 0;
|
||
}
|
||
break;
|
||
case 'B': // Move cursor down n lines
|
||
if (argslen == 1) {
|
||
if (args[0] == 0) { _termy++; } else { _termy += args[0]; }
|
||
if (_termy > obj.height) _termy = obj.height;
|
||
}
|
||
break;
|
||
case 'C': // Move cursor right n lines
|
||
if (argslen == 1) {
|
||
if (args[0] == 0) { _termx++; } else { _termx += args[0]; }
|
||
if (_termx > obj.width) _termx = obj.width;
|
||
}
|
||
break;
|
||
case 'D': // Move cursor left n lines
|
||
if (argslen == 1) {
|
||
if (args[0] == 0) { _termx--; } else { _termx -= args[0]; }
|
||
if (_termx < 0) _termx = 0;
|
||
}
|
||
break;
|
||
case 'd': // Set cursor to line n
|
||
if (argslen == 1) {
|
||
_termy = args[0] - 1;
|
||
if (_termy > obj.height) _termy = obj.height;
|
||
if (_termy < 0) _termy = 0;
|
||
}
|
||
break;
|
||
case 'G': // Set cursor to col n
|
||
if (argslen == 1) {
|
||
_termx = args[0] - 1;
|
||
if (_termx < 0) _termx = 0;
|
||
if (_termx > (obj.width - 1)) _termx = (obj.width - 1);
|
||
}
|
||
break;
|
||
case 'P': // Delete X Character(s), default 1 char
|
||
var x = 1;
|
||
if (argslen == 1) { x = args[0]; }
|
||
for (i = _termx; i < obj.width - x; i++) { _tscreen[_termy][i] = _tscreen[_termy][i + x]; _scratt[_termy][i] = _scratt[_termy][i + x]; }
|
||
for (i = (obj.width - x); i < obj.width; i++) { _tscreen[_termy][i] = ' '; _scratt[_termy][i] = (7 << 6); }
|
||
break;
|
||
case 'L': // Insert X Line(s), default 1 char
|
||
var linecount = 1;
|
||
if (argslen == 1) { linecount = args[0]; }
|
||
if (linecount == 0) { linecount = 1; }
|
||
for (y = _scrollRegion[1]; y >= _termy + linecount; y--) {
|
||
_tscreen[y] = _tscreen[y - linecount];
|
||
_scratt[y] = _scratt[y - linecount];
|
||
}
|
||
for (y = _termy; y < _termy + linecount; y++) {
|
||
_tscreen[y] = [];
|
||
_scratt[y] = [];
|
||
for (x = 0; x < obj.width; x++) { _tscreen[y][x] = ' '; _scratt[y][x] = (7 << 6); }
|
||
}
|
||
break;
|
||
case 'J': // ClearScreen:
|
||
if (argslen == 1 && args[0] == 2) {
|
||
obj.TermClear((_TermCurrentBColor << 12) + (_TermCurrentFColor << 6)); // Erase entire screen
|
||
_termx = 0;
|
||
_termy = 0;
|
||
scrollBackBuffer = [];
|
||
}
|
||
else if (argslen == 0 || argslen == 1 && args[0] == 0) // Erase cursor down
|
||
{
|
||
_EraseCursorToEol();
|
||
for (i = _termy + 1; i < obj.height; i++) _EraseLine(i);
|
||
}
|
||
else if (argslen == 1 && args[0] == 1) // Erase cursor up
|
||
{
|
||
_EraseCursorToEol();
|
||
for (i = 0; i < _termy - 1; i++) _EraseLine(i);
|
||
}
|
||
break;
|
||
case 'H': // MoveCursor:
|
||
if (argslen == 2) {
|
||
if (args[0] < 1) args[0] = 1;
|
||
if (args[1] < 1) args[1] = 1;
|
||
if (args[0] > obj.height) args[0] = obj.height;
|
||
if (args[1] > obj.width) args[1] = obj.width;
|
||
_termy = args[0] - 1;
|
||
_termx = args[1] - 1;
|
||
} else {
|
||
_termy = 0;
|
||
_termx = 0;
|
||
}
|
||
break;
|
||
case 'm': // ScreenAttribs:
|
||
// Change attributes
|
||
for (i = 0; i < argslen; i++) {
|
||
if (!args[i] || args[i] == 0) {
|
||
// Reset Attributes
|
||
_TermCurrentBColor = 0;
|
||
_TermCurrentFColor = 7;
|
||
_TermCurrentReverse = 0;
|
||
}
|
||
else if (args[i] == 1) {
|
||
// Bright
|
||
if (_TermCurrentFColor < 8) _TermCurrentFColor += 8;
|
||
}
|
||
else if (args[i] == 2 || args[i] == 22) {
|
||
// Dim
|
||
if (_TermCurrentFColor >= 8) _TermCurrentFColor -= 8;
|
||
}
|
||
else if (args[i] == 7) {
|
||
// Set Reverse attribute true
|
||
_TermCurrentReverse = 2;
|
||
}
|
||
else if (args[i] == 27) {
|
||
// Set Reverse attribute false
|
||
_TermCurrentReverse = 0;
|
||
}
|
||
else if (args[i] >= 30 && args[i] <= 37) {
|
||
// Set Foreground Color
|
||
var bright = (_TermCurrentFColor >= 8);
|
||
_TermCurrentFColor = (args[i] - 30);
|
||
if (bright && _TermCurrentFColor <= 8) _TermCurrentFColor += 8;
|
||
}
|
||
else if (args[i] >= 40 && args[i] <= 47) {
|
||
// Set Background Color
|
||
_TermCurrentBColor = (args[i] - 40);
|
||
}
|
||
else if (args[i] >= 90 && args[i] <= 99) {
|
||
// Set Bright Foreground Color
|
||
_TermCurrentFColor = (args[i] - 82);
|
||
}
|
||
else if (args[i] >= 100 && args[i] <= 109) {
|
||
// Set Bright Background Color
|
||
_TermCurrentBColor = (args[i] - 92);
|
||
}
|
||
}
|
||
break;
|
||
case 'K': // EraseLine:
|
||
if (argslen == 0 || (argslen == 1 && (!args[0] || args[0] == 0))) {
|
||
_EraseCursorToEol(); // Erase from the cursor to the end of the line
|
||
} else if (argslen == 1) {
|
||
if (args[0] == 1) { // Erase from the beginning of the line to the cursor
|
||
_EraseBolToCursor();
|
||
} else if (args[0] == 2) { // Erase the line with the cursor
|
||
_EraseLine(_termy);
|
||
}
|
||
}
|
||
break;
|
||
case 'h': // EnableLineWrap:
|
||
_TermLineWrap = true;
|
||
break;
|
||
case 'l': // DisableLineWrap:
|
||
_TermLineWrap = false;
|
||
break;
|
||
case 'r': // Set the scroll region
|
||
if (argslen == 2) { _scrollRegion = [args[0] - 1, args[1] - 1]; }
|
||
if (_scrollRegion[0] < 0) { _scrollRegion[0] = 0; }
|
||
if (_scrollRegion[0] > (obj.height - 1)) { _scrollRegion[0] = (obj.height - 1); }
|
||
if (_scrollRegion[1] < 0) { _scrollRegion[1] = 0; }
|
||
if (_scrollRegion[1] > (obj.height - 1)) { _scrollRegion[1] = (obj.height - 1); }
|
||
if (_scrollRegion[0] > _scrollRegion[1]) { _scrollRegion[0] = _scrollRegion[1]; }
|
||
break;
|
||
case 'S': // Scroll up the scroll region X lines, default 1
|
||
var x = 1;
|
||
if (argslen == 1) { x = args[0] }
|
||
for (var y = _scrollRegion[0]; y <= _scrollRegion[1] - x; y++) {
|
||
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y + x][z]; _scratt[y][z] = _scratt[y + x][z]; }
|
||
}
|
||
for (var y = _scrollRegion[1] - x + 1; y < _scrollRegion[1]; y++) {
|
||
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
|
||
}
|
||
break;
|
||
case 'M': // Delete X lines, default 1
|
||
var x = 1;
|
||
if (argslen == 1) { x = args[0] }
|
||
for (var y = _termy; y <= _scrollRegion[1] - x; y++) {
|
||
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y + x][z]; _scratt[y][z] = _scratt[y + x][z]; }
|
||
}
|
||
for (var y = _scrollRegion[1] - x + 1; y < _scrollRegion[1]; y++) {
|
||
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
|
||
}
|
||
break;
|
||
case 'T': // Scroll down the scroll region X lines, default 1
|
||
var x = 1;
|
||
if (argslen == 1) { x = args[0] }
|
||
for (var y = _scrollRegion[1]; y > _scrollRegion[0] + x; y--) {
|
||
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y - x][z]; _scratt[y][z] = _scratt[y - x][z]; }
|
||
}
|
||
for (var y = _scrollRegion[0] + x; y > _scrollRegion[0]; y--) {
|
||
for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
|
||
}
|
||
break;
|
||
case 'X': // Erase X characters, default 1 (untested)
|
||
var x = 1;
|
||
if (argslen == 1) { x = args[0] }
|
||
while ((x > 0) && (_termx > 0)) { _tscreen[_termy][_termx] = ' '; _termx--; x--; }
|
||
break;
|
||
default:
|
||
//if (code != '@') alert(code);
|
||
console.log('unknown terminal code', code, args, mode);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
obj.ProcessVt100String = function (str) {
|
||
for (var i = 0; i < str.length; i++) _ProcessVt100Char(String.fromCharCode(str.charCodeAt(i)));
|
||
}
|
||
|
||
|
||
|
||
|
||
function _ProcessVt100Char(c) {
|
||
if (c == '\0' || c.charCodeAt() == 7) return; // Ignore null & bell
|
||
var ch = c.charCodeAt();
|
||
//console.log('_ProcessVt100Char', ch, c);
|
||
|
||
|
||
|
||
|
||
//if (ch < 32 && ch != 10 && ch != 13) alert(ch);
|
||
switch (ch) {
|
||
case 16: { c = ' '; break; } // This is an odd char that show up on Intel BIOS's.
|
||
case 24: { c = '↑'; break; }
|
||
case 25: { c = '↓'; break; }
|
||
}
|
||
|
||
if (_termx > obj.width) _termx = obj.width;
|
||
if (_termy > (obj.height - 1)) _termy = (obj.height - 1);
|
||
|
||
switch (c) {
|
||
case '\b': // Backspace
|
||
if (_termx > 0) {
|
||
_termx--;
|
||
if (_backSpaceErase) { _TermDrawChar(' '); }
|
||
}
|
||
break;
|
||
case '\t': // tab
|
||
var tab = 8 - (_termx % 8)
|
||
for (var x = 0; x < tab; x++) _ProcessVt100Char(" ");
|
||
break;
|
||
case '\n': // Linefeed
|
||
_termy++;
|
||
if (_termy > _scrollRegion[1]) {
|
||
// Move everything up one line
|
||
obj.recordLineTobackBuffer(0);
|
||
_TermMoveUp(1);
|
||
_termy = _scrollRegion[1];
|
||
}
|
||
if (obj.lineFeed = '\r') { _termx = 0; } // *** If we are in Linux mode, \n will also return the cursor to the first col
|
||
break;
|
||
case '\r': // Carriage Return
|
||
_termx = 0;
|
||
break;
|
||
default:
|
||
if (_termx >= obj.width) {
|
||
_termx = 0;
|
||
if (_TermLineWrap) { _termy++; }
|
||
if (_termy >= (obj.height - 1)) { _TermMoveUp(1); _termy = (obj.height - 1); }
|
||
}
|
||
_TermDrawChar(c);
|
||
_termx++;
|
||
break;
|
||
}
|
||
}
|
||
|
||
function _TermDrawChar(c) {
|
||
_tscreen[_termy][_termx] = c;
|
||
_scratt[_termy][_termx] = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse;
|
||
}
|
||
|
||
obj.TermClear = function(TermColor) {
|
||
for (var y = 0; y < obj.height; y++) {
|
||
for (var x = 0; x < obj.width; x++) {
|
||
_tscreen[y][x] = ' ';
|
||
_scratt[y][x] = TermColor;
|
||
}
|
||
}
|
||
scrollBackBuffer = [];
|
||
}
|
||
|
||
obj.TermResetScreen = function () {
|
||
_TermCurrentReverse = 0;
|
||
_TermCurrentFColor = 7;
|
||
_TermCurrentBColor = 0;
|
||
_TermLineWrap = _cursorVisible = true;
|
||
_termx = _termy = 0;
|
||
_backSpaceErase = false;
|
||
_scrollRegion = [0, (obj.height - 1)];
|
||
_altKeypadMode = false;
|
||
obj.TermClear(7 << 6);
|
||
}
|
||
|
||
function _EraseCursorToEol() {
|
||
var t = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse;
|
||
for (var x = _termx; x < obj.width; x++) {
|
||
_tscreen[_termy][x] = ' ';
|
||
_scratt[_termy][x] = t;
|
||
}
|
||
}
|
||
|
||
function _EraseBolToCursor() {
|
||
var t = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse;
|
||
for (var x = 0; x < _termx; x++) {
|
||
_tscreen[_termy][x] = ' ';
|
||
_scratt[_termy][x] = t;
|
||
}
|
||
}
|
||
|
||
function _EraseLine(line) {
|
||
var t = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse;
|
||
for (var x = 0; x < obj.width; x++) {
|
||
_tscreen[line][x] = ' ';
|
||
_scratt[line][x] = t;
|
||
}
|
||
}
|
||
|
||
obj.TermSendKeys = function (keys) { if (obj.debugmode == 2) { console.log("TSend(" + keys.length + "): " + rstr2hex(keys), keys); } obj.parent.send(keys); }
|
||
obj.TermSendKey = function (key) { if (obj.debugmode == 2) { console.log("TSend(1): " + rstr2hex(String.fromCharCode(key)), key); } obj.parent.send(String.fromCharCode(key)); }
|
||
|
||
function _TermMoveUp(linecount) {
|
||
var x, y;
|
||
for (y = _scrollRegion[0]; y <= _scrollRegion[1] - linecount; y++) {
|
||
_tscreen[y] = _tscreen[y + linecount];
|
||
_scratt[y] = _scratt[y + linecount];
|
||
}
|
||
for (y = _scrollRegion[1] - linecount + 1; y <= _scrollRegion[1]; y++) {
|
||
_tscreen[y] = [];
|
||
_scratt[y] = [];
|
||
for (x = 0; x < obj.width; x++) {
|
||
_tscreen[y][x] = ' ';
|
||
_scratt[y][x] = (7 << 6);
|
||
}
|
||
}
|
||
}
|
||
|
||
obj.TermHandleKeys = function (e) {
|
||
if (!e.ctrlKey) {
|
||
if (e.which == 127) obj.TermSendKey(8);
|
||
else if (e.which == 13) { obj.TermSendKeys(obj.lineFeed); }
|
||
else if (e.which != 0) obj.TermSendKey(e.which);
|
||
return false;
|
||
}
|
||
if (e.preventDefault) e.preventDefault();
|
||
if (e.stopPropagation) e.stopPropagation();
|
||
}
|
||
|
||
obj.TermHandleKeyUp = function (e) {
|
||
if ((e.which != 8) && (e.which != 32) && (e.which != 9)) return true;
|
||
if (e.preventDefault) e.preventDefault();
|
||
if (e.stopPropagation) e.stopPropagation();
|
||
return false;
|
||
}
|
||
|
||
obj.TermHandleKeyDown = function (e) {
|
||
if ((e.which >= 65) && (e.which <= 90) && (e.ctrlKey == true)) {
|
||
obj.TermSendKey(e.which - 64);
|
||
if (e.preventDefault) e.preventDefault();
|
||
if (e.stopPropagation) e.stopPropagation();
|
||
return;
|
||
}
|
||
if (e.which == 27) { obj.TermSendKeys(String.fromCharCode(27)); return true; }; // ESC
|
||
|
||
if (_altKeypadMode == true) {
|
||
if (e.which == 37) { obj.TermSendKeys(String.fromCharCode(27, 79, 68)); return true; }; // Left
|
||
if (e.which == 38) { obj.TermSendKeys(String.fromCharCode(27, 79, 65)); return true; }; // Up
|
||
if (e.which == 39) { obj.TermSendKeys(String.fromCharCode(27, 79, 67)); return true; }; // Right
|
||
if (e.which == 40) { obj.TermSendKeys(String.fromCharCode(27, 79, 66)); return true; }; // Down
|
||
} else {
|
||
if (e.which == 37) { obj.TermSendKeys(String.fromCharCode(27, 91, 68)); return true; }; // Left
|
||
if (e.which == 38) { obj.TermSendKeys(String.fromCharCode(27, 91, 65)); return true; }; // Up
|
||
if (e.which == 39) { obj.TermSendKeys(String.fromCharCode(27, 91, 67)); return true; }; // Right
|
||
if (e.which == 40) { obj.TermSendKeys(String.fromCharCode(27, 91, 66)); return true; }; // Down
|
||
}
|
||
|
||
if (e.which == 33) { obj.TermSendKeys(String.fromCharCode(27, 91, 53, 126)); return true; }; // PageUp
|
||
if (e.which == 34) { obj.TermSendKeys(String.fromCharCode(27, 91, 54, 126)); return true; }; // PageDown
|
||
if (e.which == 35) { obj.TermSendKeys(String.fromCharCode(27, 91, 70)); return true; }; // End
|
||
if (e.which == 36) { obj.TermSendKeys(String.fromCharCode(27, 91, 72)); return true; }; // Home
|
||
if (e.which == 45) { obj.TermSendKeys(String.fromCharCode(27, 91, 50, 126)); return true; }; // Insert
|
||
if (e.which == 46) { obj.TermSendKeys(String.fromCharCode(27, 91, 51, 126)); return true; }; // Delete
|
||
|
||
if (e.which == 9) { obj.TermSendKeys("\t"); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return true; }; // TAB
|
||
|
||
// F1 to F12 keys
|
||
|
||
if (e.which != 8 && e.which != 32 && e.which != 9) return true;
|
||
obj.TermSendKey(e.which);
|
||
if (e.preventDefault) e.preventDefault();
|
||
if (e.stopPropagation) e.stopPropagation();
|
||
return false;
|
||
}
|
||
|
||
obj.recordLineTobackBuffer = function(y) {
|
||
var closetag = '', buf = '';
|
||
var r = obj.TermDrawLine(buf, y, closetag);
|
||
buf = r[0];
|
||
closetag = r[1];
|
||
scrollBackBuffer.push(buf + closetag + '<br>');
|
||
}
|
||
|
||
obj.TermDrawLine = function (buf, y, closetag) {
|
||
var newat, c, oldat = 1, x1, x2;
|
||
for (var x = 0; x < obj.width; ++x) {
|
||
newat = _scratt[y][x];
|
||
if (_termx == x && _termy == y && _cursorVisible) { newat |= _VTREVERSE; } // If this is the cursor location, reverse the color.
|
||
if (newat != oldat) {
|
||
buf += closetag;
|
||
closetag = '';
|
||
x1 = 6; x2 = 12;
|
||
if (newat & _VTREVERSE) { x1 = 12; x2 = 6; }
|
||
buf += '<span style="color:#' + _TermColors[(newat >> x1) & 0x3F] + ';background-color:#' + _TermColors[(newat >> x2) & 0x3F];
|
||
if (newat & _VTUNDERLINE) buf += ';text-decoration:underline';
|
||
buf += ';">';
|
||
closetag = "</span>" + closetag;
|
||
oldat = newat;
|
||
}
|
||
|
||
c = _tscreen[y][x];
|
||
switch (c) {
|
||
case '&': buf += '&'; break;
|
||
case '<': buf += '<'; break;
|
||
case '>': buf += '>'; break;
|
||
case ' ': buf += ' '; break;
|
||
default: buf += c; break;
|
||
}
|
||
}
|
||
return [buf, closetag];
|
||
}
|
||
|
||
obj.TermDraw = function() {
|
||
var closetag = '', buf = '';
|
||
for (var y = 0; y < obj.height; ++y) {
|
||
var r = obj.TermDrawLine(buf, y, closetag);
|
||
buf = r[0];
|
||
closetag = r[1];
|
||
if (y != (obj.height - 1)) buf += '<br>';
|
||
}
|
||
|
||
if (scrollBackBuffer.length > 800) { scrollBackBuffer = scrollBackBuffer.slice(scrollBackBuffer.length - 800); }
|
||
var backbuffer = scrollBackBuffer.join('');
|
||
obj.DivElement.innerHTML = "<font size='4'><b>" + backbuffer + buf + closetag + "</b></font>";
|
||
obj.DivElement.scrollTop = obj.DivElement.scrollHeight;
|
||
if (obj.heightLock == 0) { setTimeout(obj.TermLockHeight, 10); }
|
||
}
|
||
|
||
obj.TermLockHeight = function () {
|
||
obj.heightLock = obj.DivElement.clientHeight;
|
||
obj.DivElement.style['height'] = obj.DivElement.parentNode.style['height'] = obj.heightLock + 'px';
|
||
obj.DivElement.style['overflow-y'] = 'scroll';
|
||
}
|
||
|
||
obj.TermInit = function () { obj.TermResetScreen(); }
|
||
|
||
obj.heightLock = 0;
|
||
obj.DivElement.style['height'] = '';
|
||
if ((options != null) && (options.width != null) && (options.height != null)) { obj.Init(options.width, options.height); } else { obj.Init(); }
|
||
return obj;
|
||
}/* zlib.js -- JavaScript implementation for the zlib.
|
||
Version: 0.2.0
|
||
LastModified: Apr 12 2012
|
||
Copyright (C) 2012 Masanao Izumo <iz@onicos.co.jp>
|
||
|
||
The original copyright notice (zlib 1.2.6):
|
||
|
||
Copyright (C) 1995-2012 Jean-loup Gailly and Mark Adler
|
||
|
||
This software is provided 'as-is', without any express or implied
|
||
warranty. In no event will the authors be held liable for any damages
|
||
arising from the use of this software.
|
||
|
||
Permission is granted to anyone to use this software for any purpose,
|
||
including commercial applications, and to alter it and redistribute it
|
||
freely, subject to the following restrictions:
|
||
|
||
1. The origin of this software must not be misrepresented; you must not
|
||
claim that you wrote the original software. If you use this software
|
||
in a product, an acknowledgment in the product documentation would be
|
||
appreciated but is not required.
|
||
2. Altered source versions must be plainly marked as such, and must not be
|
||
misrepresented as being the original software.
|
||
3. This notice may not be removed or altered from any source distribution.
|
||
|
||
Jean-loup Gailly Mark Adler
|
||
jloup@gzip.org madler@alumni.caltech.edu
|
||
|
||
|
||
The data format used by the zlib library is described by RFCs (Request for
|
||
Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
|
||
(zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
|
||
*/
|
||
|
||
var ZLIB = ( ZLIB || {} ); // ZLIB namespace initialization
|
||
|
||
// common definitions
|
||
if(typeof ZLIB.common_initialized === 'undefined') {
|
||
ZLIB.Z_NO_FLUSH = 0;
|
||
ZLIB.Z_PARTIAL_FLUSH = 1;
|
||
ZLIB.Z_SYNC_FLUSH = 2;
|
||
ZLIB.Z_FULL_FLUSH = 3;
|
||
ZLIB.Z_FINISH = 4;
|
||
ZLIB.Z_BLOCK = 5;
|
||
ZLIB.Z_TREES = 6;
|
||
/* Allowed flush values; see deflate() and inflate() below for details */
|
||
|
||
ZLIB.Z_OK = 0;
|
||
ZLIB.Z_STREAM_END = 1;
|
||
ZLIB.Z_NEED_DICT = 2;
|
||
ZLIB.Z_ERRNO = (-1);
|
||
ZLIB.Z_STREAM_ERROR = (-2);
|
||
ZLIB.Z_DATA_ERROR = (-3);
|
||
ZLIB.Z_MEM_ERROR = (-4);
|
||
ZLIB.Z_BUF_ERROR = (-5);
|
||
ZLIB.Z_VERSION_ERROR = (-6);
|
||
/* Return codes for the compression/decompression functions. Negative values
|
||
* are errors, positive values are used for special but normal events.
|
||
*/
|
||
|
||
ZLIB.Z_DEFLATED = 8; /* The deflate compression method (the only one supported in this version) */
|
||
|
||
/**
|
||
* z_stream constructor
|
||
* @constructor
|
||
*/
|
||
ZLIB.z_stream = function() {
|
||
this.next_in = 0; /* next input byte */
|
||
this.avail_in = 0; /* number of bytes available in input_data */
|
||
this.total_in = 0; /* total number of input bytes read so far */
|
||
|
||
this.next_out = 0; /* next output byte */
|
||
this.avail_out = 0; /* remaining free space at next_out */
|
||
this.total_out = 0; /* total number of bytes output so far */
|
||
|
||
this.msg = null; /* last error message, null if no error */
|
||
this.state = null; /* not visible by applications */
|
||
|
||
this.data_type = 0; /* best guess about the data type: binary or text */
|
||
this.adler = 0; /* TODO: adler32 value of the uncompressed data */
|
||
|
||
// zlib.js
|
||
this.input_data = ''; /* input data */
|
||
this.output_data = ''; /* output data */
|
||
this.error = 0; /* error code */
|
||
this.checksum_function = null; /* crc32(for gzip) or adler32(for zlib) */
|
||
};
|
||
|
||
/**
|
||
* TODO
|
||
* @constructor
|
||
*/
|
||
ZLIB.gz_header = function() {
|
||
this.text = 0; /* true if compressed data believed to be text */
|
||
this.time = 0; /* modification time */
|
||
this.xflags = 0; /* extra flags (not used when writing a gzip file) */
|
||
this.os = 0xff; /* operating system */
|
||
this.extra = null; /* extra field string or null if none */
|
||
this.extra_len = 0; /* this.extra.length (only when reading header) */
|
||
this.extra_max = 0; /* space at extra (only when reading header) */
|
||
this.name = null; /* file name string or null if none */
|
||
this.name_max = 0; /* space at name (only when reading header) */
|
||
this.comment = null; /* comment string or null if none */
|
||
this.comm_max = 0; /* space at comment (only when reading header) */
|
||
this.hcrc = 0; /* true if there was or will be a header crc */
|
||
this.done = 0; /* true when done reading gzip header (not used
|
||
when writing a gzip file) */
|
||
};
|
||
|
||
ZLIB.common_initialized = true;
|
||
} // common definitions
|
||
/* zlib-inflate.js -- JavaScript implementation for the zlib inflate.
|
||
Version: 0.2.0
|
||
LastModified: Apr 12 2012
|
||
Copyright (C) 2012 Masanao Izumo <iz@onicos.co.jp>
|
||
|
||
This library is one of the JavaScript zlib implementation.
|
||
Some API's are modified from the original.
|
||
Only inflate API is implemented.
|
||
|
||
The original copyright notice (zlib 1.2.6):
|
||
|
||
Copyright (C) 1995-2012 Jean-loup Gailly and Mark Adler
|
||
|
||
This software is provided 'as-is', without any express or implied
|
||
warranty. In no event will the authors be held liable for any damages
|
||
arising from the use of this software.
|
||
|
||
Permission is granted to anyone to use this software for any purpose,
|
||
including commercial applications, and to alter it and redistribute it
|
||
freely, subject to the following restrictions:
|
||
|
||
1. The origin of this software must not be misrepresented; you must not
|
||
claim that you wrote the original software. If you use this software
|
||
in a product, an acknowledgment in the product documentation would be
|
||
appreciated but is not required.
|
||
2. Altered source versions must be plainly marked as such, and must not be
|
||
misrepresented as being the original software.
|
||
3. This notice may not be removed or altered from any source distribution.
|
||
|
||
Jean-loup Gailly Mark Adler
|
||
jloup@gzip.org madler@alumni.caltech.edu
|
||
|
||
|
||
The data format used by the zlib library is described by RFCs (Request for
|
||
Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
|
||
(zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
|
||
*/
|
||
|
||
/*
|
||
API documentation
|
||
==============================================================================
|
||
Usage: z_stream = ZLIB.inflateInit([windowBits]);
|
||
|
||
Create the stream object for decompression.
|
||
See zlib.h for windowBits information.
|
||
|
||
==============================================================================
|
||
Usage: decoded_string = z_stream.inflate(encoded_string [, {OPTIONS...}]);
|
||
|
||
OPTIONS:
|
||
next_in: decode start offset for encoded_string.
|
||
|
||
avail_in: // TODO document. See zlib.h for the information.
|
||
|
||
avail_out: // TODO document. See zlib.h for the information.
|
||
|
||
flush: // TODO document. See zlib.h for the information.
|
||
|
||
Ex: decoded_string = z_stream.inflate(encoded_string);
|
||
decoded_string = z_stream.inflate(encoded_string,
|
||
{next_in: 0,
|
||
avail_in: encoded_string.length,
|
||
avail_out: 1024,
|
||
flush: ZLIB.Z_NO_FLUSH});
|
||
|
||
See zlib.h for more information.
|
||
|
||
==============================================================================
|
||
Usage: z_stream.inflateReset();
|
||
TODO document
|
||
|
||
*/
|
||
|
||
if( typeof ZLIB === 'undefined' ) {
|
||
alert('ZLIB is not defined. SRC zlib.js before zlib-inflate.js')
|
||
}
|
||
|
||
(function() {
|
||
|
||
/* inflate.c -- zlib decompression
|
||
* Copyright (C) 1995-2011 Mark Adler
|
||
* For conditions of distribution and use, see copyright notice in zlib.h
|
||
*/
|
||
|
||
var DEF_WBITS = 15;
|
||
|
||
// inflate_mode
|
||
var HEAD = 0; /* i: waiting for magic header */
|
||
var FLAGS = 1; /* i: waiting for method and flags (gzip) */
|
||
var TIME = 2; /* i: waiting for modification time (gzip) */
|
||
var OS = 3; /* i: waiting for extra flags and operating system (gzip) */
|
||
var EXLEN = 4; /* i: waiting for extra length (gzip) */
|
||
var EXTRA = 5; /* i: waiting for extra bytes (gzip) */
|
||
var NAME = 6; /* i: waiting for end of file name (gzip) */
|
||
var COMMENT = 7; /* i: waiting for end of comment (gzip) */
|
||
var HCRC = 8; /* i: waiting for header crc (gzip) */
|
||
var DICTID = 9; /* i: waiting for dictionary check value */
|
||
var DICT = 10; /* waiting for inflateSetDictionary() call */
|
||
var TYPE = 11; /* i: waiting for type bits, including last-flag bit */
|
||
var TYPEDO = 12; /* i: same, but skip check to exit inflate on new block */
|
||
var STORED = 13; /* i: waiting for stored size (length and complement) */
|
||
var COPY_ = 14; /* i/o: same as COPY below, but only first time in */
|
||
var COPY = 15; /* i/o: waiting for input or output to copy stored block */
|
||
var TABLE = 16; /* i: waiting for dynamic block table lengths */
|
||
var LENLENS = 17; /* i: waiting for code length code lengths */
|
||
var CODELENS = 18; /* i: waiting for length/lit and distance code lengths */
|
||
var LEN_ = 19; /* i: same as LEN below, but only first time in */
|
||
var LEN = 20; /* i: waiting for length/lit/eob code */
|
||
var LENEXT = 21; /* i: waiting for length extra bits */
|
||
var DIST = 22; /* i: waiting for distance code */
|
||
var DISTEXT = 23; /* i: waiting for distance extra bits */
|
||
var MATCH = 24; /* o: waiting for output space to copy string */
|
||
var LIT = 25; /* o: waiting for output space to write literal */
|
||
var CHECK = 26; /* i: waiting for 32-bit check value */
|
||
var LENGTH = 27; /* i: waiting for 32-bit length (gzip) */
|
||
var DONE = 28; /* finished check, done -- remain here until reset */
|
||
var BAD = 29; /* got a data error -- remain here until reset */
|
||
var MEM = 30; /* got an inflate() memory error -- remain here until reset */
|
||
var SYNC = 31; /* looking for synchronization bytes to restart inflate() */
|
||
|
||
/* Maximum size of the dynamic table. The maximum number of code structures is
|
||
1444, which is the sum of 852 for literal/length codes and 592 for distance
|
||
codes. These values were found by exhaustive searches using the program
|
||
examples/enough.c found in the zlib distribtution. The arguments to that
|
||
program are the number of symbols, the initial root table size, and the
|
||
maximum bit length of a code. "enough 286 9 15" for literal/length codes
|
||
returns returns 852, and "enough 30 6 15" for distance codes returns 592.
|
||
The initial root table size (9 or 6) is found in the fifth argument of the
|
||
inflate_table() calls in inflate.c and infback.c. If the root table size is
|
||
changed, then these maximum sizes would be need to be recalculated and
|
||
updated. */
|
||
var ENOUGH_LENS = 852;
|
||
var ENOUGH_DISTS = 592;
|
||
var ENOUGH = (ENOUGH_LENS + ENOUGH_DISTS);
|
||
|
||
/* Type of code to build for inflate_table() */
|
||
var CODES = 0;
|
||
var LENS = 1;
|
||
var DISTS = 2;
|
||
|
||
|
||
|
||
var inflate_table_lbase = [ /* Length codes 257..285 base */
|
||
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
|
||
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0];
|
||
var inflate_table_lext = [ /* Length codes 257..285 extra */
|
||
16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
|
||
19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 203, 69];
|
||
var inflate_table_dbase = [ /* Distance codes 0..29 base */
|
||
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
|
||
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
|
||
8193, 12289, 16385, 24577, 0, 0];
|
||
var inflate_table_dext = [ /* Distance codes 0..29 extra */
|
||
16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
|
||
23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
|
||
28, 28, 29, 29, 64, 64];
|
||
|
||
/* inftrees.c -- generate Huffman trees for efficient decoding
|
||
* Copyright (C) 1995-2012 Mark Adler
|
||
* For conditions of distribution and use, see copyright notice in zlib.h
|
||
*/
|
||
|
||
ZLIB.inflate_copyright =
|
||
' inflate 1.2.6 Copyright 1995-2012 Mark Adler ';
|
||
/*
|
||
If you use the zlib library in a product, an acknowledgment is welcome
|
||
in the documentation of your product. If for some reason you cannot
|
||
include such an acknowledgment, I would appreciate that you keep this
|
||
copyright string in the executable of your product.
|
||
*/
|
||
|
||
/*
|
||
Build a set of tables to decode the provided canonical Huffman code.
|
||
The code lengths are lens[0..codes-1]. The result starts at *table,
|
||
whose indices are 0..2^bits-1. work is a writable array of at least
|
||
lens shorts, which is used as a work area. type is the type of code
|
||
to be generated, CODES, LENS, or DISTS. On return, zero is success,
|
||
-1 is an invalid code, and +1 means that ENOUGH isn't enough. table
|
||
on return points to the next available entry's address. bits is the
|
||
requested root table index bits, and on return it is the actual root
|
||
table index bits. It will differ if the request is greater than the
|
||
longest code or if it is less than the shortest code.
|
||
*/
|
||
function inflate_table(state, type)
|
||
{
|
||
var MAXBITS = 15;
|
||
var table = state.next;
|
||
var bits = (type == DISTS ? state.distbits : state.lenbits);
|
||
var work = state.work;
|
||
var lens = state.lens;
|
||
var lens_offset = (type == DISTS ? state.nlen : 0);
|
||
var state_codes = state.codes;
|
||
var codes;
|
||
if(type == LENS)
|
||
codes = state.nlen;
|
||
else if(type == DISTS)
|
||
codes = state.ndist;
|
||
else // CODES
|
||
codes = 19;
|
||
|
||
var len; /* a code's length in bits */
|
||
var sym; /* index of code symbols */
|
||
var min, max; /* minimum and maximum code lengths */
|
||
var root; /* number of index bits for root table */
|
||
var curr; /* number of index bits for current table */
|
||
var drop; /* code bits to drop for sub-table */
|
||
var left; /* number of prefix codes available */
|
||
var used; /* code entries in table used */
|
||
var huff; /* Huffman code */
|
||
var incr; /* for incrementing code, index */
|
||
var fill; /* index for replicating entries */
|
||
var low; /* low bits for current root entry */
|
||
var mask; /* mask for low root bits */
|
||
var here; /* table entry for duplication */
|
||
var next; /* next available space in table */
|
||
var base; /* base value table to use */
|
||
var base_offset;
|
||
var extra; /* extra bits table to use */
|
||
var extra_offset;
|
||
var end; /* use base and extra for symbol > end */
|
||
var count = new Array(MAXBITS+1); /* number of codes of each length */
|
||
var offs = new Array(MAXBITS+1); /* offsets in table for each length */
|
||
|
||
/*
|
||
Process a set of code lengths to create a canonical Huffman code. The
|
||
code lengths are lens[0..codes-1]. Each length corresponds to the
|
||
symbols 0..codes-1. The Huffman code is generated by first sorting the
|
||
symbols by length from short to long, and retaining the symbol order
|
||
for codes with equal lengths. Then the code starts with all zero bits
|
||
for the first code of the shortest length, and the codes are integer
|
||
increments for the same length, and zeros are appended as the length
|
||
increases. For the deflate format, these bits are stored backwards
|
||
from their more natural integer increment ordering, and so when the
|
||
decoding tables are built in the large loop below, the integer codes
|
||
are incremented backwards.
|
||
|
||
This routine assumes, but does not check, that all of the entries in
|
||
lens[] are in the range 0..MAXBITS. The caller must assure this.
|
||
1..MAXBITS is interpreted as that code length. zero means that that
|
||
symbol does not occur in this code.
|
||
|
||
The codes are sorted by computing a count of codes for each length,
|
||
creating from that a table of starting indices for each length in the
|
||
sorted table, and then entering the symbols in order in the sorted
|
||
table. The sorted table is work[], with that space being provided by
|
||
the caller.
|
||
|
||
The length counts are used for other purposes as well, i.e. finding
|
||
the minimum and maximum length codes, determining if there are any
|
||
codes at all, checking for a valid set of lengths, and looking ahead
|
||
at length counts to determine sub-table sizes when building the
|
||
decoding tables.
|
||
*/
|
||
|
||
/* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
|
||
for (len = 0; len <= MAXBITS; len++)
|
||
count[len] = 0;
|
||
for (sym = 0; sym < codes; sym++)
|
||
count[lens[lens_offset + sym]]++;
|
||
|
||
/* bound code lengths, force root to be within code lengths */
|
||
root = bits;
|
||
|
||
for (max = MAXBITS; max >= 1; max--)
|
||
if (count[max] != 0) break;
|
||
if (root > max) root = max;
|
||
if (max == 0) {
|
||
/* no symbols to code at all */
|
||
/* invalid code marker */
|
||
here = {op:64, bits:1, val:0};
|
||
state_codes[table++] = here; /* make a table to force an error */
|
||
state_codes[table++] = here;
|
||
if(type == DISTS) state.distbits = 1; else state.lenbits = 1; // *bits = 1;
|
||
state.next = table;
|
||
return 0; /* no symbols, but wait for decoding to report error */
|
||
}
|
||
for (min = 1; min < max; min++)
|
||
if (count[min] != 0) break;
|
||
if (root < min) root = min;
|
||
|
||
/* check for an over-subscribed or incomplete set of lengths */
|
||
left = 1;
|
||
for (len = 1; len <= MAXBITS; len++) {
|
||
left <<= 1;
|
||
left -= count[len];
|
||
if (left < 0) return -1; /* over-subscribed */
|
||
}
|
||
if (left > 0 && (type == CODES || max != 1)) {
|
||
state.next = table;
|
||
return -1; /* incomplete set */
|
||
}
|
||
|
||
/* generate offsets into symbol table for each length for sorting */
|
||
offs[1] = 0;
|
||
for (len = 1; len < MAXBITS; len++)
|
||
offs[len + 1] = offs[len] + count[len];
|
||
|
||
/* sort symbols by length, by symbol order within each length */
|
||
for (sym = 0; sym < codes; sym++)
|
||
if (lens[lens_offset + sym] != 0) work[offs[lens[lens_offset + sym]]++] = sym;
|
||
|
||
/*
|
||
Create and fill in decoding tables. In this loop, the table being
|
||
filled is at next and has curr index bits. The code being used is huff
|
||
with length len. That code is converted to an index by dropping drop
|
||
bits off of the bottom. For codes where len is less than drop + curr,
|
||
those top drop + curr - len bits are incremented through all values to
|
||
fill the table with replicated entries.
|
||
|
||
root is the number of index bits for the root table. When len exceeds
|
||
root, sub-tables are created pointed to by the root entry with an index
|
||
of the low root bits of huff. This is saved in low to check for when a
|
||
new sub-table should be started. drop is zero when the root table is
|
||
being filled, and drop is root when sub-tables are being filled.
|
||
|
||
When a new sub-table is needed, it is necessary to look ahead in the
|
||
code lengths to determine what size sub-table is needed. The length
|
||
counts are used for this, and so count[] is decremented as codes are
|
||
entered in the tables.
|
||
|
||
used keeps track of how many table entries have been allocated from the
|
||
provided *table space. It is checked for LENS and DIST tables against
|
||
the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in
|
||
the initial root table size constants. See the comments in inftrees.h
|
||
for more information.
|
||
|
||
sym increments through all symbols, and the loop terminates when
|
||
all codes of length max, i.e. all codes, have been processed. This
|
||
routine permits incomplete codes, so another loop after this one fills
|
||
in the rest of the decoding tables with invalid code markers.
|
||
*/
|
||
|
||
/* set up for code type */
|
||
switch (type) {
|
||
case CODES:
|
||
base = extra = work; /* dummy value--not used */
|
||
base_offset = 0;
|
||
extra_offset = 0;
|
||
end = 19;
|
||
break;
|
||
case LENS:
|
||
base = inflate_table_lbase;
|
||
base_offset = -257; // base -= 257;
|
||
extra = inflate_table_lext;
|
||
extra_offset = -257; // extra -= 257;
|
||
end = 256;
|
||
break;
|
||
default: /* DISTS */
|
||
base = inflate_table_dbase;
|
||
extra = inflate_table_dext;
|
||
base_offset = 0;
|
||
extra_offset = 0;
|
||
end = -1;
|
||
}
|
||
|
||
/* initialize state for loop */
|
||
huff = 0; /* starting code */
|
||
sym = 0; /* starting code symbol */
|
||
len = min; /* starting code length */
|
||
next = table; /* current table to fill in */
|
||
curr = root; /* current table index bits */
|
||
drop = 0; /* current bits to drop from code for index */
|
||
low = -1; /* trigger new sub-table when len > root */
|
||
used = 1 << root; /* use root table entries */
|
||
mask = used - 1; /* mask for comparing low */
|
||
|
||
/* check available table space */
|
||
if ((type == LENS && used >= ENOUGH_LENS) ||
|
||
(type == DISTS && used >= ENOUGH_DISTS)) {
|
||
state.next = table;
|
||
return 1;
|
||
}
|
||
|
||
/* process all codes and make table entries */
|
||
for (;;) {
|
||
/* create table entry */
|
||
here = {op:0, bits:len - drop, val:0};
|
||
if (work[sym] < end) {
|
||
here.val = work[sym];
|
||
}
|
||
else if (work[sym] > end) {
|
||
here.op = extra[extra_offset + work[sym]];
|
||
here.val = base[base_offset + work[sym]];
|
||
}
|
||
else {
|
||
here.op = 32 + 64; /* end of block */
|
||
}
|
||
|
||
/* replicate for those indices with low len bits equal to huff */
|
||
incr = 1 << (len - drop);
|
||
fill = 1 << curr;
|
||
min = fill; /* save offset to next table */
|
||
do {
|
||
fill -= incr;
|
||
state_codes[next + (huff >>> drop) + fill] = here;
|
||
} while (fill != 0);
|
||
|
||
/* backwards increment the len-bit code huff */
|
||
incr = 1 << (len - 1);
|
||
while (huff & incr)
|
||
incr >>>= 1;
|
||
if (incr != 0) {
|
||
huff &= incr - 1;
|
||
huff += incr;
|
||
}
|
||
else
|
||
huff = 0;
|
||
|
||
/* go to next symbol, update count, len */
|
||
sym++;
|
||
if (--(count[len]) == 0) {
|
||
if (len == max) break;
|
||
len = lens[lens_offset + work[sym]];
|
||
}
|
||
|
||
/* create new sub-table if needed */
|
||
if (len > root && (huff & mask) != low) {
|
||
/* if first time, transition to sub-tables */
|
||
if (drop == 0)
|
||
drop = root;
|
||
|
||
/* increment past last table */
|
||
next += min; /* here min is 1 << curr */
|
||
|
||
/* determine length of next table */
|
||
curr = len - drop;
|
||
left = (1 << curr);
|
||
while (curr + drop < max) {
|
||
left -= count[curr + drop];
|
||
if (left <= 0) break;
|
||
curr++;
|
||
left <<= 1;
|
||
}
|
||
|
||
/* check for enough space */
|
||
used += 1 << curr;
|
||
if ((type == LENS && used >= ENOUGH_LENS) ||
|
||
(type == DISTS && used >= ENOUGH_DISTS)) {
|
||
state.next = table;
|
||
return 1;
|
||
}
|
||
|
||
/* point entry in root table to sub-table */
|
||
low = huff & mask;
|
||
state_codes[table + low] = {op:curr, bits:root, val:next - table};
|
||
}
|
||
}
|
||
|
||
/* fill in remaining table entry if code is incomplete (guaranteed to have
|
||
at most one remaining entry, since if the code is incomplete, the
|
||
maximum code length that was allowed to get this far is one bit) */
|
||
if (huff != 0) {
|
||
state_codes[next + huff] = {op:64, bits:len - drop, val:0};
|
||
}
|
||
|
||
/* set return parameters */
|
||
state.next = table + used;
|
||
if(type == DISTS) state.distbits = root; else state.lenbits = root; //*bits = root;
|
||
return 0;
|
||
}
|
||
|
||
/* inffast.c -- fast decoding
|
||
* Copyright (C) 1995-2008, 2010 Mark Adler
|
||
* For conditions of distribution and use, see copyright notice in zlib.h
|
||
*/
|
||
|
||
/*
|
||
Decode literal, length, and distance codes and write out the resulting
|
||
literal and match bytes until either not enough input or output is
|
||
available, an end-of-block is encountered, or a data error is encountered.
|
||
When large enough input and output buffers are supplied to inflate(), for
|
||
example, a 16K input buffer and a 64K output buffer, more than 95% of the
|
||
inflate execution time is spent in this routine.
|
||
|
||
Entry assumptions:
|
||
|
||
state->mode == LEN
|
||
strm->avail_in >= 6
|
||
strm->avail_out >= 258
|
||
start >= strm->avail_out
|
||
state->bits < 8
|
||
|
||
On return, state->mode is one of:
|
||
|
||
LEN -- ran out of enough output space or enough available input
|
||
TYPE -- reached end of block code, inflate() to interpret next block
|
||
BAD -- error in block data
|
||
|
||
Notes:
|
||
|
||
- The maximum input bits used by a length/distance pair is 15 bits for the
|
||
length code, 5 bits for the length extra, 15 bits for the distance code,
|
||
and 13 bits for the distance extra. This totals 48 bits, or six bytes.
|
||
Therefore if strm->avail_in >= 6, then there is enough input to avoid
|
||
checking for available input while decoding.
|
||
|
||
- The maximum bytes that a single length/distance pair can output is 258
|
||
bytes, which is the maximum length that can be coded. inflate_fast()
|
||
requires strm->avail_out >= 258 for each loop to avoid checking for
|
||
output space.
|
||
*/
|
||
function inflate_fast(strm,
|
||
start) /* inflate()'s starting value for strm->avail_out */
|
||
{
|
||
var state;
|
||
var input_data; /* local strm->input_data */
|
||
var next_in; /* zlib.js: index of input_data */
|
||
var last; /* while next_in < last, enough input available */
|
||
var out; /* local strm.next_out */
|
||
var beg; /* inflate()'s initial strm.next_out */
|
||
var end; /* while out < end, enough space available */
|
||
//NOSPRT #ifdef INFLATE_STRICT
|
||
// unsigned dmax; /* maximum distance from zlib header */
|
||
//#endif
|
||
var wsize; /* window size or zero if not using window */
|
||
var whave; /* valid bytes in the window */
|
||
var wnext; /* window write index */
|
||
var window; /* allocated sliding window, if wsize != 0 */
|
||
var hold; /* local strm->hold */
|
||
var bits; /* local strm->bits */
|
||
var codes; /* zlib.js: local state.codes */
|
||
var lcode; /* local strm->lencode */
|
||
var dcode; /* local strm->distcode */
|
||
var lmask; /* mask for first level of length codes */
|
||
var dmask; /* mask for first level of distance codes */
|
||
var here; /* retrieved table entry */
|
||
var op; /* code bits, operation, extra bits, or */
|
||
/* window position, window bytes to copy */
|
||
var len; /* match length, unused bytes */
|
||
var dist; /* match distance */
|
||
// var from; /* where to copy match from */
|
||
var from_window_offset = -1; /* index of window[] */
|
||
var from_out_offset = -1; /* index of next_out[] */
|
||
|
||
/* copy state to local variables */
|
||
state = strm.state;
|
||
input_data = strm.input_data;
|
||
next_in = strm.next_in;
|
||
last = next_in + strm.avail_in - 5;
|
||
out = strm.next_out;
|
||
beg = out - (start - strm.avail_out);
|
||
end = out + (strm.avail_out - 257);
|
||
//NOSPRT #ifdef INFLATE_STRICT
|
||
// dmax = state->dmax;
|
||
//#endif
|
||
wsize = state.wsize;
|
||
whave = state.whave;
|
||
wnext = state.wnext;
|
||
window = state.window;
|
||
hold = state.hold;
|
||
bits = state.bits;
|
||
codes = state.codes;
|
||
lcode = state.lencode;
|
||
dcode = state.distcode;
|
||
lmask = (1 << state.lenbits) - 1;
|
||
dmask = (1 << state.distbits) - 1;
|
||
|
||
/* decode literals and length/distances until end-of-block or not enough
|
||
input data or output space */
|
||
loop: do {
|
||
if (bits < 15) {
|
||
hold += (input_data.charCodeAt(next_in++) & 0xff) << bits;
|
||
bits += 8;
|
||
hold += (input_data.charCodeAt(next_in++) & 0xff) << bits;
|
||
bits += 8;
|
||
}
|
||
here = codes[lcode + (hold & lmask)];
|
||
dolen: while(true) {
|
||
op = here.bits;
|
||
hold >>>= op;
|
||
bits -= op;
|
||
op = here.op;
|
||
if (op == 0) { /* literal */
|
||
// Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
|
||
// "inflate: literal '%c'\n" :
|
||
// "inflate: literal 0x%02x\n", here.val));
|
||
strm.output_data += String.fromCharCode(here.val);
|
||
out++;
|
||
}
|
||
else if (op & 16) { /* length base */
|
||
len = here.val;
|
||
op &= 15; /* number of extra bits */
|
||
if (op) {
|
||
if (bits < op) {
|
||
hold += (input_data.charCodeAt(next_in++) & 0xff) << bits;
|
||
bits += 8;
|
||
}
|
||
len += hold & ((1 << op) - 1);
|
||
hold >>>= op;
|
||
bits -= op;
|
||
}
|
||
// Tracevv((stderr, "inflate: length %u\n", len));
|
||
if (bits < 15) {
|
||
hold += (input_data.charCodeAt(next_in++) & 0xff) << bits;
|
||
bits += 8;
|
||
hold += (input_data.charCodeAt(next_in++) & 0xff) << bits;
|
||
bits += 8;
|
||
}
|
||
here = codes[dcode + (hold & dmask)];
|
||
dodist: while(true) {
|
||
op = here.bits;
|
||
hold >>>= op;
|
||
bits -= op;
|
||
op = here.op;
|
||
if (op & 16) { /* distance base */
|
||
dist = here.val;
|
||
op &= 15; /* number of extra bits */
|
||
if (bits < op) {
|
||
hold += (input_data.charCodeAt(next_in++) & 0xff) << bits;
|
||
bits += 8;
|
||
if (bits < op) {
|
||
hold += (input_data.charCodeAt(next_in++) & 0xff) << bits;
|
||
bits += 8;
|
||
}
|
||
}
|
||
dist += hold & ((1 << op) - 1);
|
||
//NOSPRT #ifdef INFLATE_STRICT
|
||
// if (dist > dmax) {
|
||
// strm->msg = (char *)"invalid distance too far back";
|
||
// state->mode = BAD;
|
||
// break loop;
|
||
// }
|
||
//#endif
|
||
hold >>>= op;
|
||
bits -= op;
|
||
// Tracevv((stderr, "inflate: distance %u\n", dist));
|
||
op = out - beg; /* max distance in output */
|
||
if (dist > op) { /* see if copy from window */
|
||
op = dist - op; /* distance back in window */
|
||
if (op > whave) {
|
||
if (state.sane) {
|
||
strm.msg = 'invalid distance too far back';
|
||
state.mode = BAD;
|
||
break loop;
|
||
}
|
||
//NOSPRT #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
|
||
// if (len <= op - whave) {
|
||
// do {
|
||
// PUP(out) = 0;
|
||
// } while (--len);
|
||
// continue;
|
||
// }
|
||
// len -= op - whave;
|
||
// do {
|
||
// PUP(out) = 0;
|
||
// } while (--op > whave);
|
||
// if (op == 0) {
|
||
// from = out - dist;
|
||
// do {
|
||
// PUP(out) = PUP(from);
|
||
// } while (--len);
|
||
// continue;
|
||
// }
|
||
//#endif
|
||
} // if (op > whave)
|
||
|
||
from_window_offset = 0;
|
||
from_out_offset = -1;
|
||
if (wnext == 0) { /* very common case */
|
||
from_window_offset += wsize - op;
|
||
if (op < len) { /* some from window */
|
||
len -= op;
|
||
strm.output_data += window.substring(from_window_offset, from_window_offset + op);
|
||
out += op;
|
||
op = 0;
|
||
from_window_offset = -1;
|
||
from_out_offset = out - dist; /* rest from output */
|
||
}
|
||
}
|
||
//NOTREACHED else if (wnext < op) { /* wrap around window */
|
||
//NOTREACHED from += wsize + wnext - op;
|
||
//NOTREACHED op -= wnext;
|
||
//NOTREACHED if (op < len) { /* some from end of window */
|
||
//NOTREACHED len -= op;
|
||
//NOTREACHED do {
|
||
//NOTREACHED PUP(out) = PUP(from);
|
||
//NOTREACHED } while (--op);
|
||
//NOTREACHED from = window - OFF;
|
||
//NOTREACHED if (wnext < len) { /* some from start of window */
|
||
//NOTREACHED op = wnext;
|
||
//NOTREACHED len -= op;
|
||
//NOTREACHED do {
|
||
//NOTREACHED PUP(out) = PUP(from);
|
||
//NOTREACHED } while (--op);
|
||
//NOTREACHED from = out - dist; /* rest from output */
|
||
//NOTREACHED }
|
||
//NOTREACHED }
|
||
//NOTREACHED }
|
||
else { /* contiguous in window */
|
||
from_window_offset += wnext - op;
|
||
if (op < len) { /* some from window */
|
||
len -= op;
|
||
strm.output_data += window.substring(from_window_offset, from_window_offset + op);
|
||
out += op;
|
||
from_window_offset = -1;
|
||
from_out_offset = out - dist; /* rest from output */
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
from_window_offset = -1;
|
||
from_out_offset = out - dist; /* copy direct from output */
|
||
}
|
||
|
||
if (from_window_offset >= 0) {
|
||
strm.output_data += window.substring(from_window_offset, from_window_offset + len);
|
||
out += len;
|
||
from_window_offset += len;
|
||
} else {
|
||
var len_inner = len;
|
||
if(len_inner > out - from_out_offset)
|
||
len_inner = out - from_out_offset;
|
||
strm.output_data += strm.output_data.substring(
|
||
from_out_offset, from_out_offset + len_inner);
|
||
out += len_inner;
|
||
len -= len_inner;
|
||
from_out_offset += len_inner;
|
||
out += len;
|
||
while (len > 2) {
|
||
strm.output_data += strm.output_data.charAt(from_out_offset++);
|
||
strm.output_data += strm.output_data.charAt(from_out_offset++);
|
||
strm.output_data += strm.output_data.charAt(from_out_offset++);
|
||
len -= 3;
|
||
}
|
||
if (len) {
|
||
strm.output_data += strm.output_data.charAt(from_out_offset++);
|
||
if (len > 1)
|
||
strm.output_data += strm.output_data.charAt(from_out_offset++);
|
||
}
|
||
}
|
||
}
|
||
else if ((op & 64) == 0) { /* 2nd level distance code */
|
||
here = codes[dcode + (here.val + (hold & ((1 << op) - 1)))];
|
||
continue dodist; // goto dodist
|
||
}
|
||
else {
|
||
strm.msg = 'invalid distance code';
|
||
state.mode = BAD;
|
||
break loop;
|
||
}
|
||
break dodist; }
|
||
}
|
||
else if ((op & 64) == 0) { /* 2nd level length code */
|
||
here = codes[lcode + (here.val + (hold & ((1 << op) - 1)))];
|
||
continue dolen; // goto dolen;
|
||
}
|
||
else if (op & 32) { /* end-of-block */
|
||
// Tracevv((stderr, "inflate: end of block\n"));
|
||
state.mode = TYPE;
|
||
break loop;
|
||
}
|
||
else {
|
||
strm.msg = 'invalid literal/length code';
|
||
state.mode = BAD;
|
||
break loop;
|
||
}
|
||
break dolen; }
|
||
} while (next_in < last && out < end);
|
||
|
||
/* return unused bytes (on entry, bits < 8, so in won't go too far back) */
|
||
len = bits >>> 3;
|
||
next_in -= len;
|
||
bits -= len << 3;
|
||
hold &= (1 << bits) - 1;
|
||
|
||
/* update state and return */
|
||
strm.next_in = next_in;
|
||
strm.next_out = out;
|
||
strm.avail_in = (next_in < last ? 5 + (last - next_in) : 5 - (next_in - last));
|
||
strm.avail_out = (out < end ?
|
||
257 + (end - out) : 257 - (out - end));
|
||
state.hold = hold;
|
||
state.bits = bits;
|
||
}
|
||
|
||
function new_array(size)
|
||
{
|
||
var i;
|
||
var ary = new Array(size);
|
||
for(i = 0; i < size; i++)
|
||
ary[i] = 0;
|
||
return ary;
|
||
}
|
||
|
||
function getarg(opts, name, def_value)
|
||
{
|
||
return (opts && (name in opts)) ? opts[name] : def_value;
|
||
}
|
||
|
||
function checksum_none()
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* z_stream constructor
|
||
* @constructor
|
||
*/
|
||
function inflate_state()
|
||
{
|
||
var i;
|
||
|
||
this.mode = 0; /* current inflate mode */
|
||
this.last = 0; /* true if processing last block */
|
||
this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */
|
||
this.havedict = 0; /* true if dictionary provided */
|
||
this.flags = 0; /* gzip header method and flags (0 if zlib) */
|
||
this.dmax = 0; /* zlib header max distance (INFLATE_STRICT) */
|
||
this.check = 0; /* protected copy of check value */
|
||
this.total = 0; /* protected copy of output count */
|
||
this.head = null; /* where to save gzip header information */
|
||
/* sliding window */
|
||
this.wbits = 0; /* log base 2 of requested window size */
|
||
this.wsize = 0; /* window size or zero if not using window */
|
||
this.whave = 0; /* valid bytes in the window */
|
||
this.wnext = 0; /* window write index (TODO remove) */
|
||
this.window = null; /* allocated sliding window, if needed */
|
||
/* bit accumulator */
|
||
this.hold = 0; /* input bit accumulator */
|
||
this.bits = 0; /* number of bits in "in" */
|
||
/* for string and stored block copying */
|
||
this.length = 0; /* literal or length of data to copy */
|
||
this.offset = 0; /* distance back to copy string from */
|
||
/* for table and code decoding */
|
||
this.extra = 0; /* extra bits needed */
|
||
/* fixed and dynamic code tables */
|
||
|
||
/* zlib.js: modified implementation: lencode, distcode, next are offset of codes[] */
|
||
this.lencode = 0; /* starting table for length/literal codes */
|
||
this.distcode = 0; /* starting table for distance codes */
|
||
this.lenbits = 0; /* index bits for lencode */
|
||
this.distbits = 0; /* index bits for distcode */
|
||
/* dynamic table building */
|
||
this.ncode = 0; /* number of code length code lengths */
|
||
this.nlen = 0; /* number of length code lengths */
|
||
this.ndist = 0; /* number of distance code lengths */
|
||
this.have = 0; /* number of code lengths in lens[] */
|
||
this.next = 0; /* next available space in codes[] */
|
||
this.lens = new_array(320); /* temporary storage for code lengths */
|
||
this.work = new_array(288); /* work area for code table building */
|
||
this.codes = new Array(ENOUGH); /* space for code tables */
|
||
var c = {op:0, bits:0, val:0};
|
||
for(i = 0; i < ENOUGH; i++)
|
||
this.codes[i] = c;
|
||
this.sane = 0; /* if false, allow invalid distance too far */
|
||
this.back = 0; /* bits back of last unprocessed length/lit */
|
||
this.was = 0; /* initial length of match */
|
||
}
|
||
|
||
ZLIB.inflateResetKeep = function(strm)
|
||
{
|
||
var state;
|
||
|
||
if (!strm || !strm.state) return ZLIB.Z_STREAM_ERROR;
|
||
state = strm.state;
|
||
strm.total_in = strm.total_out = state.total = 0;
|
||
strm.msg = null;
|
||
if (state.wrap) { /* to support ill-conceived Java test suite */
|
||
strm.adler = state.wrap & 1;
|
||
}
|
||
|
||
state.mode = HEAD;
|
||
state.last = 0;
|
||
state.havedict = 0;
|
||
state.dmax = 32768;
|
||
state.head = null;
|
||
state.hold = 0;
|
||
state.bits = 0;
|
||
state.lencode = 0;
|
||
state.distcode = 0;
|
||
state.next = 0;
|
||
state.sane = 1;
|
||
state.back = -1;
|
||
return ZLIB.Z_OK;
|
||
};
|
||
|
||
// Usage: strm = ZLIB.inflateReset(z_stream [, windowBits]);
|
||
ZLIB.inflateReset = function(strm, windowBits)
|
||
{
|
||
var wrap;
|
||
var state;
|
||
|
||
/* get the state */
|
||
if (!strm || !strm.state) return ZLIB.Z_STREAM_ERROR;
|
||
state = strm.state;
|
||
|
||
if(typeof windowBits === "undefined")
|
||
windowBits = DEF_WBITS;
|
||
|
||
/* extract wrap request from windowBits parameter */
|
||
if (windowBits < 0) {
|
||
wrap = 0;
|
||
windowBits = -windowBits;
|
||
}
|
||
else {
|
||
wrap = (windowBits >>> 4) + 1;
|
||
if (windowBits < 48)
|
||
windowBits &= 15;
|
||
}
|
||
|
||
if(wrap == 1 && (typeof ZLIB.adler32 === 'function')) {
|
||
strm.checksum_function = ZLIB.adler32;
|
||
} else if(wrap == 2 && (typeof ZLIB.crc32 === 'function')) {
|
||
strm.checksum_function = ZLIB.crc32;
|
||
} else {
|
||
strm.checksum_function = checksum_none;
|
||
}
|
||
|
||
/* set number of window bits, free window if different */
|
||
if (windowBits && (windowBits < 8 || windowBits > 15))
|
||
return ZLIB.Z_STREAM_ERROR;
|
||
if (state.window && state.wbits != windowBits) {
|
||
state.window = null;
|
||
}
|
||
|
||
/* update state and reset the rest of it */
|
||
state.wrap = wrap;
|
||
state.wbits = windowBits;
|
||
state.wsize = 0;
|
||
state.whave = 0;
|
||
state.wnext = 0;
|
||
return ZLIB.inflateResetKeep(strm);
|
||
};
|
||
|
||
// Usage: strm = ZLIB.inflateInit([windowBits]);
|
||
ZLIB.inflateInit = function(windowBits)
|
||
{
|
||
var strm = new ZLIB.z_stream();
|
||
strm.state = new inflate_state();
|
||
ZLIB.inflateReset(strm, windowBits);
|
||
return strm;
|
||
};
|
||
|
||
ZLIB.inflatePrime = function(strm, bits, value)
|
||
{
|
||
var state;
|
||
|
||
if (!strm || !strm.state) return ZLIB.Z_STREAM_ERROR;
|
||
state = strm.state;
|
||
if (bits < 0) {
|
||
state.hold = 0;
|
||
state.bits = 0;
|
||
return ZLIB.Z_OK;
|
||
}
|
||
if (bits > 16 || state.bits + bits > 32) return ZLIB.Z_STREAM_ERROR;
|
||
value &= (1 << bits) - 1;
|
||
state.hold += value << state.bits;
|
||
state.bits += bits;
|
||
return ZLIB.Z_OK;
|
||
};
|
||
|
||
var lenfix_ary = null;
|
||
var distfix_ary = null;
|
||
function fixedtables(state)
|
||
{
|
||
var i;
|
||
if (!lenfix_ary) lenfix_ary = [ { op: 96, bits: 7, val: 0 }, { op: 0, bits: 8, val: 80 }, { op: 0, bits: 8, val: 16 }, { op: 20, bits: 8, val: 115 }, { op: 18, bits: 7, val: 31 }, { op: 0, bits: 8, val: 112 }, { op: 0, bits: 8, val: 48 }, { op: 0, bits: 9, val: 192 }, { op: 16, bits: 7, val: 10 }, { op: 0, bits: 8, val: 96 }, { op: 0, bits: 8, val: 32 }, { op: 0, bits: 9, val: 160 }, { op: 0, bits: 8, val: 0 }, { op: 0, bits: 8, val: 128 }, { op: 0, bits: 8, val: 64 }, { op: 0, bits: 9, val: 224 }, { op: 16, bits: 7, val: 6 }, { op: 0, bits: 8, val: 88 }, { op: 0, bits: 8, val: 24 }, { op: 0, bits: 9, val: 144 }, { op: 19, bits: 7, val: 59 }, { op: 0, bits: 8, val: 120 }, { op: 0, bits: 8, val: 56 }, { op: 0, bits: 9, val: 208 }, { op: 17, bits: 7, val: 17 }, { op: 0, bits: 8, val: 104 }, { op: 0, bits: 8, val: 40 }, { op: 0, bits: 9, val: 176 }, { op: 0, bits: 8, val: 8 }, { op: 0, bits: 8, val: 136 }, { op: 0, bits: 8, val: 72 }, { op: 0, bits: 9, val: 240 }, { op: 16, bits: 7, val: 4 }, { op: 0, bits: 8, val: 84 }, { op: 0, bits: 8, val: 20 }, { op: 21, bits: 8, val: 227 }, { op: 19, bits: 7, val: 43 }, { op: 0, bits: 8, val: 116 }, { op: 0, bits: 8, val: 52 }, { op: 0, bits: 9, val: 200 }, { op: 17, bits: 7, val: 13 }, { op: 0, bits: 8, val: 100 }, { op: 0, bits: 8, val: 36 }, { op: 0, bits: 9, val: 168 }, { op: 0, bits: 8, val: 4 }, { op: 0, bits: 8, val: 132 }, { op: 0, bits: 8, val: 68 }, { op: 0, bits: 9, val: 232 }, { op: 16, bits: 7, val: 8 }, { op: 0, bits: 8, val: 92 }, { op: 0, bits: 8, val: 28 }, { op: 0, bits: 9, val: 152 }, { op: 20, bits: 7, val: 83 }, { op: 0, bits: 8, val: 124 }, { op: 0, bits: 8, val: 60 }, { op: 0, bits: 9, val: 216 }, { op: 18, bits: 7, val: 23 }, { op: 0, bits: 8, val: 108 }, { op: 0, bits: 8, val: 44 }, { op: 0, bits: 9, val: 184 }, { op: 0, bits: 8, val: 12 }, { op: 0, bits: 8, val: 140 }, { op: 0, bits: 8, val: 76 }, { op: 0, bits: 9, val: 248 }, { op: 16, bits: 7, val: 3 }, { op: 0, bits: 8, val: 82 }, { op: 0, bits: 8, val: 18 }, { op: 21, bits: 8, val: 163 }, { op: 19, bits: 7, val: 35 }, { op: 0, bits: 8, val: 114 }, { op: 0, bits: 8, val: 50 }, { op: 0, bits: 9, val: 196 }, { op: 17, bits: 7, val: 11 }, { op: 0, bits: 8, val: 98 }, { op: 0, bits: 8, val: 34 }, { op: 0, bits: 9, val: 164 }, { op: 0, bits: 8, val: 2 }, { op: 0, bits: 8, val: 130 }, { op: 0, bits: 8, val: 66 }, { op: 0, bits: 9, val: 228 }, { op: 16, bits: 7, val: 7 }, { op: 0, bits: 8, val: 90 }, { op: 0, bits: 8, val: 26 }, { op: 0, bits: 9, val: 148 }, { op: 20, bits: 7, val: 67 }, { op: 0, bits: 8, val: 122 }, { op: 0, bits: 8, val: 58 }, { op: 0, bits: 9, val: 212 }, { op: 18, bits: 7, val: 19 }, { op: 0, bits: 8, val: 106 }, { op: 0, bits: 8, val: 42 }, { op: 0, bits: 9, val: 180 }, { op: 0, bits: 8, val: 10 }, { op: 0, bits: 8, val: 138 }, { op: 0, bits: 8, val: 74 }, { op: 0, bits: 9, val: 244 }, { op: 16, bits: 7, val: 5 }, { op: 0, bits: 8, val: 86 }, { op: 0, bits: 8, val: 22 }, { op: 64, bits: 8, val: 0 }, { op: 19, bits: 7, val: 51 }, { op: 0, bits: 8, val: 118 }, { op: 0, bits: 8, val: 54 }, { op: 0, bits: 9, val: 204 }, { op: 17, bits: 7, val: 15 }, { op: 0, bits: 8, val: 102 }, { op: 0, bits: 8, val: 38 }, { op: 0, bits: 9, val: 172 }, { op: 0, bits: 8, val: 6 }, { op: 0, bits: 8, val: 134 }, { op: 0, bits: 8, val: 70 }, { op: 0, bits: 9, val: 236 }, { op: 16, bits: 7, val: 9 }, { op: 0, bits: 8, val: 94 }, { op: 0, bits: 8, val: 30 }, { op: 0, bits: 9, val: 156 }, { op: 20, bits: 7, val: 99 }, { op: 0, bits: 8, val: 126 }, { op: 0, bits: 8, val: 62 }, { op: 0, bits: 9, val: 220 }, { op: 18, bits: 7, val: 27 }, { op: 0, bits: 8, val: 110 }, { op: 0, bits: 8, val: 46 }, { op: 0, bits: 9, val: 188 }, { op: 0, bits: 8, val: 14 }, { op: 0, bits: 8, val: 142 }, { op: 0, bits: 8, val: 78 }, { op: 0, bits: 9, val: 252 }, { op: 96, bits: 7, val: 0 }, { op: 0, bits: 8, val: 81 }, { op: 0, bits: 8, val: 17 }, { op: 21, bits: 8, val: 131 }, { op: 18, bits: 7, val: 31 }, { op: 0, bits: 8, val: 113 }, { op: 0, bits: 8, val: 49 }, { op: 0, bits: 9, val: 194 }, { op: 16, bits: 7, val: 10 }, { op: 0, bits: 8, val: 97 }, { op: 0, bits: 8, val: 33 }, { op: 0, bits: 9, val: 162 }, { op: 0, bits: 8, val: 1 }, { op: 0, bits: 8, val: 129 }, { op: 0, bits: 8, val: 65 }, { op: 0, bits: 9, val: 226 }, { op: 16, bits: 7, val: 6 }, { op: 0, bits: 8, val: 89 }, { op: 0, bits: 8, val: 25 }, { op: 0, bits: 9, val: 146 }, { op: 19, bits: 7, val: 59 }, { op: 0, bits: 8, val: 121 }, { op: 0, bits: 8, val: 57 }, { op: 0, bits: 9, val: 210 }, { op: 17, bits: 7, val: 17 }, { op: 0, bits: 8, val: 105 }, { op: 0, bits: 8, val: 41 }, { op: 0, bits: 9, val: 178 }, { op: 0, bits: 8, val: 9 }, { op: 0, bits: 8, val: 137 }, { op: 0, bits: 8, val: 73 }, { op: 0, bits: 9, val: 242 }, { op: 16, bits: 7, val: 4 }, { op: 0, bits: 8, val: 85 }, { op: 0, bits: 8, val: 21 }, { op: 16, bits: 8, val: 258 }, { op: 19, bits: 7, val: 43 }, { op: 0, bits: 8, val: 117 }, { op: 0, bits: 8, val: 53 }, { op: 0, bits: 9, val: 202 }, { op: 17, bits: 7, val: 13 }, { op: 0, bits: 8, val: 101 }, { op: 0, bits: 8, val: 37 }, { op: 0, bits: 9, val: 170 }, { op: 0, bits: 8, val: 5 }, { op: 0, bits: 8, val: 133 }, { op: 0, bits: 8, val: 69 }, { op: 0, bits: 9, val: 234 }, { op: 16, bits: 7, val: 8 }, { op: 0, bits: 8, val: 93 }, { op: 0, bits: 8, val: 29 }, { op: 0, bits: 9, val: 154 }, { op: 20, bits: 7, val: 83 }, { op: 0, bits: 8, val: 125 }, { op: 0, bits: 8, val: 61 }, { op: 0, bits: 9, val: 218 }, { op: 18, bits: 7, val: 23 }, { op: 0, bits: 8, val: 109 }, { op: 0, bits: 8, val: 45 }, { op: 0, bits: 9, val: 186 }, { op: 0, bits: 8, val: 13 }, { op: 0, bits: 8, val: 141 }, { op: 0, bits: 8, val: 77 }, { op: 0, bits: 9, val: 250 }, { op: 16, bits: 7, val: 3 }, { op: 0, bits: 8, val: 83 }, { op: 0, bits: 8, val: 19 }, { op: 21, bits: 8, val: 195 }, { op: 19, bits: 7, val: 35 }, { op: 0, bits: 8, val: 115 }, { op: 0, bits: 8, val: 51 }, { op: 0, bits: 9, val: 198 }, { op: 17, bits: 7, val: 11 }, { op: 0, bits: 8, val: 99 }, { op: 0, bits: 8, val: 35 }, { op: 0, bits: 9, val: 166 }, { op: 0, bits: 8, val: 3 }, { op: 0, bits: 8, val: 131 }, { op: 0, bits: 8, val: 67 }, { op: 0, bits: 9, val: 230 }, { op: 16, bits: 7, val: 7 }, { op: 0, bits: 8, val: 91 }, { op: 0, bits: 8, val: 27 }, { op: 0, bits: 9, val: 150 }, { op: 20, bits: 7, val: 67 }, { op: 0, bits: 8, val: 123 }, { op: 0, bits: 8, val: 59 }, { op: 0, bits: 9, val: 214 }, { op: 18, bits: 7, val: 19 }, { op: 0, bits: 8, val: 107 }, { op: 0, bits: 8, val: 43 }, { op: 0, bits: 9, val: 182 }, { op: 0, bits: 8, val: 11 }, { op: 0, bits: 8, val: 139 }, { op: 0, bits: 8, val: 75 }, { op: 0, bits: 9, val: 246 }, { op: 16, bits: 7, val: 5 }, { op: 0, bits: 8, val: 87 }, { op: 0, bits: 8, val: 23 }, { op: 64, bits: 8, val: 0 }, { op: 19, bits: 7, val: 51 }, { op: 0, bits: 8, val: 119 }, { op: 0, bits: 8, val: 55 }, { op: 0, bits: 9, val: 206 }, { op: 17, bits: 7, val: 15 }, { op: 0, bits: 8, val: 103 }, { op: 0, bits: 8, val: 39 }, { op: 0, bits: 9, val: 174 }, { op: 0, bits: 8, val: 7 }, { op: 0, bits: 8, val: 135 }, { op: 0, bits: 8, val: 71 }, { op: 0, bits: 9, val: 238 }, { op: 16, bits: 7, val: 9 }, { op: 0, bits: 8, val: 95 }, { op: 0, bits: 8, val: 31 }, { op: 0, bits: 9, val: 158 }, { op: 20, bits: 7, val: 99 }, { op: 0, bits: 8, val: 127 }, { op: 0, bits: 8, val: 63 }, { op: 0, bits: 9, val: 222 }, { op: 18, bits: 7, val: 27 }, { op: 0, bits: 8, val: 111 }, { op: 0, bits: 8, val: 47 }, { op: 0, bits: 9, val: 190 }, { op: 0, bits: 8, val: 15 }, { op: 0, bits: 8, val: 143 }, { op: 0, bits: 8, val: 79 }, { op: 0, bits: 9, val: 254 }, { op: 96, bits: 7, val: 0 }, { op: 0, bits: 8, val: 80 }, { op: 0, bits: 8, val: 16 }, { op: 20, bits: 8, val: 115 }, { op: 18, bits: 7, val: 31 }, { op: 0, bits: 8, val: 112 }, { op: 0, bits: 8, val: 48 }, { op: 0, bits: 9, val: 193 }, { op: 16, bits: 7, val: 10 }, { op: 0, bits: 8, val: 96 }, { op: 0, bits: 8, val: 32 }, { op: 0, bits: 9, val: 161 }, { op: 0, bits: 8, val: 0 }, { op: 0, bits: 8, val: 128 }, { op: 0, bits: 8, val: 64 }, { op: 0, bits: 9, val: 225 }, { op: 16, bits: 7, val: 6 }, { op: 0, bits: 8, val: 88 }, { op: 0, bits: 8, val: 24 }, { op: 0, bits: 9, val: 145 }, { op: 19, bits: 7, val: 59 }, { op: 0, bits: 8, val: 120 }, { op: 0, bits: 8, val: 56 }, { op: 0, bits: 9, val: 209 }, { op: 17, bits: 7, val: 17 }, { op: 0, bits: 8, val: 104 }, { op: 0, bits: 8, val: 40 }, { op: 0, bits: 9, val: 177 }, { op: 0, bits: 8, val: 8 }, { op: 0, bits: 8, val: 136 }, { op: 0, bits: 8, val: 72 }, { op: 0, bits: 9, val: 241 }, { op: 16, bits: 7, val: 4 }, { op: 0, bits: 8, val: 84 }, { op: 0, bits: 8, val: 20 }, { op: 21, bits: 8, val: 227 }, { op: 19, bits: 7, val: 43 }, { op: 0, bits: 8, val: 116 }, { op: 0, bits: 8, val: 52 }, { op: 0, bits: 9, val: 201 }, { op: 17, bits: 7, val: 13 }, { op: 0, bits: 8, val: 100 }, { op: 0, bits: 8, val: 36 }, { op: 0, bits: 9, val: 169 }, { op: 0, bits: 8, val: 4 }, { op: 0, bits: 8, val: 132 }, { op: 0, bits: 8, val: 68 }, { op: 0, bits: 9, val: 233 }, { op: 16, bits: 7, val: 8 }, { op: 0, bits: 8, val: 92 }, { op: 0, bits: 8, val: 28 }, { op: 0, bits: 9, val: 153 }, { op: 20, bits: 7, val: 83 }, { op: 0, bits: 8, val: 124 }, { op: 0, bits: 8, val: 60 }, { op: 0, bits: 9, val: 217 }, { op: 18, bits: 7, val: 23 }, { op: 0, bits: 8, val: 108 }, { op: 0, bits: 8, val: 44 }, { op: 0, bits: 9, val: 185 }, { op: 0, bits: 8, val: 12 }, { op: 0, bits: 8, val: 140 }, { op: 0, bits: 8, val: 76 }, { op: 0, bits: 9, val: 249 }, { op: 16, bits: 7, val: 3 }, { op: 0, bits: 8, val: 82 }, { op: 0, bits: 8, val: 18 }, { op: 21, bits: 8, val: 163 }, { op: 19, bits: 7, val: 35 }, { op: 0, bits: 8, val: 114 }, { op: 0, bits: 8, val: 50 }, { op: 0, bits: 9, val: 197 }, { op: 17, bits: 7, val: 11 }, { op: 0, bits: 8, val: 98 }, { op: 0, bits: 8, val: 34 }, { op: 0, bits: 9, val: 165 }, { op: 0, bits: 8, val: 2 }, { op: 0, bits: 8, val: 130 }, { op: 0, bits: 8, val: 66 }, { op: 0, bits: 9, val: 229 }, { op: 16, bits: 7, val: 7 }, { op: 0, bits: 8, val: 90 }, { op: 0, bits: 8, val: 26 }, { op: 0, bits: 9, val: 149 }, { op: 20, bits: 7, val: 67 }, { op: 0, bits: 8, val: 122 }, { op: 0, bits: 8, val: 58 },{ op: 0, bits: 9, val: 213 }, { op: 18, bits: 7, val: 19 }, { op: 0, bits: 8, val: 106 }, { op: 0, bits: 8, val: 42 }, { op: 0, bits: 9, val: 181 }, { op: 0, bits: 8, val: 10 }, { op: 0, bits: 8, val: 138 },{ op: 0, bits: 8, val: 74 }, { op: 0, bits: 9, val: 245 }, { op: 16, bits: 7, val: 5 }, { op: 0, bits: 8, val: 86 }, { op: 0, bits: 8, val: 22 }, { op: 64, bits: 8, val: 0 }, { op: 19, bits: 7, val: 51 },{ op: 0, bits: 8, val: 118 }, { op: 0, bits: 8, val: 54 }, { op: 0, bits: 9, val: 205 }, { op: 17, bits: 7, val: 15 }, { op: 0, bits: 8, val: 102 }, { op: 0, bits: 8, val: 38 }, { op: 0, bits: 9, val: 173 },{ op: 0, bits: 8, val: 6 }, { op: 0, bits: 8, val: 134 }, { op: 0, bits: 8, val: 70 }, { op: 0, bits: 9, val: 237 }, { op: 16, bits: 7, val: 9 }, { op: 0, bits: 8, val: 94 }, { op: 0, bits: 8, val: 30 },{ op: 0, bits: 9, val: 157 }, { op: 20, bits: 7, val: 99 }, { op: 0, bits: 8, val: 126 }, { op: 0, bits: 8, val: 62 }, { op: 0, bits: 9, val: 221 }, { op: 18, bits: 7, val: 27 }, { op: 0, bits: 8, val: 110 }, { op: 0, bits: 8, val: 46 }, { op: 0, bits: 9, val: 189 }, { op: 0, bits: 8, val: 14 }, { op: 0, bits: 8, val: 142 }, { op: 0, bits: 8, val: 78 }, { op: 0, bits: 9, val: 253 }, { op: 96, bits: 7, val: 0 }, { op: 0, bits: 8, val: 81 }, { op: 0, bits: 8, val: 17 }, { op: 21, bits: 8, val: 131 }, { op: 18, bits: 7, val: 31 }, { op: 0, bits: 8, val: 113 }, { op: 0, bits: 8, val: 49 }, { op: 0, bits: 9, val: 195 }, { op: 16, bits: 7, val: 10 }, { op: 0, bits: 8, val: 97 }, { op: 0, bits: 8, val: 33 }, { op: 0, bits: 9, val: 163 }, { op: 0, bits: 8, val: 1 }, { op: 0, bits: 8, val: 129 }, { op: 0, bits: 8, val: 65 }, { op: 0, bits: 9, val: 227 }, { op: 16, bits: 7, val: 6 }, { op: 0, bits: 8, val: 89 }, { op: 0, bits: 8, val: 25 }, { op: 0, bits: 9, val: 147 }, { op: 19, bits: 7, val: 59 }, { op: 0, bits: 8, val: 121 }, { op: 0, bits: 8, val: 57 }, { op: 0, bits: 9, val: 211 }, { op: 17, bits: 7, val: 17 }, { op: 0, bits: 8, val: 105 }, { op: 0, bits: 8, val: 41 }, { op: 0, bits: 9, val: 179 }, { op: 0, bits: 8, val: 9 },{ op: 0, bits: 8, val: 137 }, { op: 0, bits: 8, val: 73 }, { op: 0, bits: 9, val: 243 }, { op: 16, bits: 7, val: 4 }, { op: 0, bits: 8, val: 85 }, { op: 0, bits: 8, val: 21 }, { op: 16, bits: 8, val: 258 },{ op: 19, bits: 7, val: 43 }, { op: 0, bits: 8, val: 117 }, { op: 0, bits: 8, val: 53 }, { op: 0, bits: 9, val: 203 }, { op: 17, bits: 7, val: 13 }, { op: 0, bits: 8, val: 101 }, { op: 0, bits: 8, val: 37 },{ op: 0, bits: 9, val: 171 }, { op: 0, bits: 8, val: 5 }, { op: 0, bits: 8, val: 133 }, { op: 0, bits: 8, val: 69 }, { op: 0, bits: 9, val: 235 }, { op: 16, bits: 7, val: 8 }, { op: 0, bits: 8, val: 93 },{ op: 0, bits: 8, val: 29 }, { op: 0, bits: 9, val: 155 }, { op: 20, bits: 7, val: 83 }, { op: 0, bits: 8, val: 125 }, { op: 0, bits: 8, val: 61 }, { op: 0, bits: 9, val: 219 }, { op: 18, bits: 7, val: 23 },{ op: 0, bits: 8, val: 109 }, { op: 0, bits: 8, val: 45 }, { op: 0, bits: 9, val: 187 }, { op: 0, bits: 8, val: 13 }, { op: 0, bits: 8, val: 141 }, { op: 0, bits: 8, val: 77 }, { op: 0, bits: 9, val: 251 }, { op: 16, bits: 7, val: 3 }, { op: 0, bits: 8, val: 83 }, { op: 0, bits: 8, val: 19 }, { op: 21, bits: 8, val: 195 }, { op: 19, bits: 7, val: 35 }, { op: 0, bits: 8, val: 115 }, { op: 0, bits: 8, val: 51 }, { op: 0, bits: 9, val: 199 }, { op: 17, bits: 7, val: 11 }, { op: 0, bits: 8, val: 99 }, { op: 0, bits: 8, val: 35 }, { op: 0, bits: 9, val: 167 }, { op: 0, bits: 8, val: 3 }, { op: 0, bits: 8, val: 131 }, { op: 0, bits: 8, val: 67 }, { op: 0, bits: 9, val: 231 }, { op: 16, bits: 7, val: 7 }, { op: 0, bits: 8, val: 91 }, { op: 0, bits: 8, val: 27 }, { op: 0, bits: 9, val: 151 }, { op: 20, bits: 7, val: 67 }, { op: 0, bits: 8, val: 123 }, { op: 0, bits: 8, val: 59 }, { op: 0, bits: 9, val: 215 }, { op: 18, bits: 7, val: 19 }, { op: 0, bits: 8, val: 107 }, { op: 0, bits: 8, val: 43 }, { op: 0, bits: 9, val: 183 }, { op: 0, bits: 8, val: 11 }, { op: 0, bits: 8, val: 139 }, { op: 0, bits: 8, val: 75 }, { op: 0, bits: 9, val: 247 }, { op: 16, bits: 7, val: 5 }, { op: 0, bits: 8, val: 87 }, { op: 0, bits: 8, val: 23 }, { op: 64, bits: 8, val: 0 }, { op: 19, bits: 7, val: 51 }, { op: 0, bits: 8, val: 119 }, { op: 0, bits: 8, val: 55 }, { op: 0, bits: 9, val: 207 }, { op: 17, bits: 7, val: 15 }, { op: 0, bits: 8, val: 103 }, { op: 0, bits: 8, val: 39 }, { op: 0, bits: 9, val: 175 }, { op: 0, bits: 8, val: 7 }, { op: 0, bits: 8, val: 135 }, { op: 0, bits: 8, val: 71 }, { op: 0, bits: 9, val: 239 }, { op: 16, bits: 7, val: 9 }, { op: 0, bits: 8, val: 95 }, { op: 0, bits: 8, val: 31 }, { op: 0, bits: 9, val: 159 }, { op: 20, bits: 7, val: 99 }, { op: 0, bits: 8, val: 127 }, { op: 0, bits: 8, val: 63 }, { op: 0, bits: 9, val: 223 }, { op: 18, bits: 7, val: 27 }, { op: 0, bits: 8, val: 111 }, { op: 0, bits: 8, val: 47 }, { op: 0, bits: 9, val: 191 }, { op: 0, bits: 8, val: 15 }, { op: 0, bits: 8, val: 143 }, { op: 0, bits: 8, val: 79 }, { op: 0, bits: 9, val: 255 } ];
|
||
if (!distfix_ary) distfix_ary = [ { op: 16, bits: 5, val: 1 }, { op: 23, bits: 5, val: 257 }, { op: 19, bits: 5, val: 17 }, { op: 27, bits: 5, val: 4097 }, { op: 17, bits: 5, val: 5 }, { op: 25, bits: 5, val: 1025 }, { op: 21, bits: 5, val: 65 }, { op: 29, bits: 5, val: 16385 }, { op: 16, bits: 5, val: 3 }, { op: 24, bits: 5, val: 513 }, { op: 20, bits: 5, val: 33 }, { op: 28, bits: 5, val: 8193 }, { op: 18, bits: 5, val: 9 }, { op: 26, bits: 5, val: 2049 }, { op: 22, bits: 5, val: 129 }, { op: 64, bits: 5, val: 0 }, { op: 16, bits: 5, val: 2 }, { op: 23, bits: 5, val: 385 }, { op: 19, bits: 5, val: 25 }, { op: 27, bits: 5, val: 6145 }, { op: 17, bits: 5, val: 7 }, { op: 25, bits: 5, val: 1537 }, { op: 21, bits: 5, val: 97 }, { op: 29, bits: 5, val: 24577 }, { op: 16, bits: 5, val: 4 }, { op: 24, bits: 5, val: 769 }, { op: 20, bits: 5, val: 49 }, { op: 28, bits: 5, val: 12289 }, { op: 18, bits: 5, val: 13 }, { op: 26, bits: 5, val: 3073 }, { op: 22, bits: 5, val: 193 }, { op: 64, bits: 5, val: 0 } ];
|
||
state.lencode = 0;
|
||
state.distcode = 512;
|
||
for (i = 0; i < 512; i++) { state.codes[i] = lenfix_ary[i]; }
|
||
for (i = 0; i < 32; i++) { state.codes[i + 512] = distfix_ary[i]; }
|
||
state.lenbits = 9;
|
||
state.distbits = 5;
|
||
}
|
||
|
||
/*
|
||
Update the window with the last wsize (normally 32K) bytes written before
|
||
returning. If window does not exist yet, create it. This is only called
|
||
when a window is already in use, or when output has been written during this
|
||
inflate call, but the end of the deflate stream has not been reached yet.
|
||
It is also called to create a window for dictionary data when a dictionary
|
||
is loaded.
|
||
|
||
Providing output buffers larger than 32K to inflate() should provide a speed
|
||
advantage, since only the last 32K of output is copied to the sliding window
|
||
upon return from inflate(), and since all distances after the first 32K of
|
||
output will fall in the output data, making match copies simpler and faster.
|
||
The advantage may be dependent on the size of the processor's data caches.
|
||
*/
|
||
function updatewindow(strm)
|
||
{
|
||
var state = strm.state;
|
||
var out = strm.output_data.length;
|
||
|
||
/* if it hasn't been done already, allocate space for the window */
|
||
if (state.window === null) {
|
||
state.window = '';
|
||
}
|
||
|
||
/* if window not in use yet, initialize */
|
||
if (state.wsize == 0) {
|
||
state.wsize = 1 << state.wbits;
|
||
}
|
||
|
||
// zlib.js: Sliding window
|
||
if (out >= state.wsize) {
|
||
state.window = strm.output_data.substring(out - state.wsize);
|
||
} else {
|
||
if(state.whave + out < state.wsize) {
|
||
state.window += strm.output_data;
|
||
} else {
|
||
state.window = state.window.substring(state.whave - (state.wsize - out)) + strm.output_data;
|
||
}
|
||
}
|
||
state.whave = state.window.length;
|
||
if(state.whave < state.wsize) {
|
||
state.wnext = state.whave;
|
||
} else {
|
||
state.wnext = 0;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
// #ifdef GUNZIP
|
||
function CRC2(strm, word)
|
||
{
|
||
var hbuf = [word & 0xff, (word >>> 8) & 0xff];
|
||
strm.state.check = strm.checksum_function(strm.state.check, hbuf, 0, 2);
|
||
}
|
||
|
||
function CRC4(strm, word)
|
||
{
|
||
var hbuf = [word & 0xff,
|
||
(word >>> 8) & 0xff,
|
||
(word >>> 16) & 0xff,
|
||
(word >>> 24) & 0xff];
|
||
strm.state.check = strm.checksum_function(strm.state.check, hbuf, 0, 4);
|
||
}
|
||
|
||
/* Load registers with state in inflate() for speed */
|
||
function LOAD(strm, s)
|
||
{
|
||
s.strm = strm; /* z_stream */
|
||
s.left = strm.avail_out; /* available output */
|
||
s.next = strm.next_in; /* next input */
|
||
s.have = strm.avail_in; /* available input */
|
||
s.hold = strm.state.hold; /* bit buffer */
|
||
s.bits = strm.state.bits; /* bits in bit buffer */
|
||
return s;
|
||
}
|
||
|
||
/* Restore state from registers in inflate() */
|
||
function RESTORE(s)
|
||
{
|
||
var strm = s.strm;
|
||
strm.next_in = s.next;
|
||
strm.avail_out = s.left;
|
||
strm.avail_in = s.have;
|
||
strm.state.hold = s.hold;
|
||
strm.state.bits = s.bits;
|
||
}
|
||
|
||
/* Clear the input bit accumulator */
|
||
function INITBITS(s)
|
||
{
|
||
s.hold = 0;
|
||
s.bits = 0;
|
||
}
|
||
|
||
/* Get a byte of input into the bit accumulator, or return from inflate()
|
||
if there is no input available. */
|
||
function PULLBYTE(s)
|
||
{
|
||
if (s.have == 0) return false;
|
||
s.have--;
|
||
s.hold += (s.strm.input_data.charCodeAt(s.next++) & 0xff) << s.bits;
|
||
s.bits += 8;
|
||
return true;
|
||
}
|
||
|
||
/* Assure that there are at least n bits in the bit accumulator. If there is
|
||
not enough available input to do that, then return from inflate(). */
|
||
function NEEDBITS(s, n)
|
||
{
|
||
// if(typeof n != 'number') throw 'ERROR';
|
||
while (s.bits < n) {
|
||
if(!PULLBYTE(s))
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/* Return the low n bits of the bit accumulator (n < 16) */
|
||
function BITS(s, n)
|
||
{
|
||
return s.hold & ((1 << n) - 1);
|
||
}
|
||
|
||
/* Remove n bits from the bit accumulator */
|
||
function DROPBITS(s, n)
|
||
{
|
||
// if(typeof n != 'number') throw 'ERROR';
|
||
s.hold >>>= n;
|
||
s.bits -= n;
|
||
}
|
||
|
||
/* Remove zero to seven bits as needed to go to a byte boundary */
|
||
function BYTEBITS(s)
|
||
{
|
||
s.hold >>>= s.bits & 7;
|
||
s.bits -= s.bits & 7;
|
||
}
|
||
|
||
/* Reverse the bytes in a 32-bit value */
|
||
function REVERSE(q)
|
||
{
|
||
return ((q >>> 24) & 0xff) +
|
||
((q >>> 8) & 0xff00) +
|
||
((q & 0xff00) << 8) +
|
||
((q & 0xff) << 24);
|
||
}
|
||
|
||
/*
|
||
inflate() uses a state machine to process as much input data and generate as
|
||
much output data as possible before returning. The state machine is
|
||
structured roughly as follows:
|
||
|
||
for (;;) switch (state) {
|
||
...
|
||
case STATEn:
|
||
if (not enough input data or output space to make progress)
|
||
return;
|
||
... make progress ...
|
||
state = STATEm;
|
||
break;
|
||
...
|
||
}
|
||
|
||
so when inflate() is called again, the same case is attempted again, and
|
||
if the appropriate resources are provided, the machine proceeds to the
|
||
next state. The NEEDBITS() macro is usually the way the state evaluates
|
||
whether it can proceed or should return. NEEDBITS() does the return if
|
||
the requested bits are not available. The typical use of the BITS macros
|
||
is:
|
||
|
||
NEEDBITS(n);
|
||
... do something with BITS(n) ...
|
||
DROPBITS(n);
|
||
|
||
where NEEDBITS(n) either returns from inflate() if there isn't enough
|
||
input left to load n bits into the accumulator, or it continues. BITS(n)
|
||
gives the low n bits in the accumulator. When done, DROPBITS(n) drops
|
||
the low n bits off the accumulator. INITBITS() clears the accumulator
|
||
and sets the number of available bits to zero. BYTEBITS() discards just
|
||
enough bits to put the accumulator on a byte boundary. After BYTEBITS()
|
||
and a NEEDBITS(8), then BITS(8) would return the next byte in the stream.
|
||
|
||
NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return
|
||
if there is no input available. The decoding of variable length codes uses
|
||
PULLBYTE() directly in order to pull just enough bytes to decode the next
|
||
code, and no more.
|
||
|
||
Some states loop until they get enough input, making sure that enough
|
||
state information is maintained to continue the loop where it left off
|
||
if NEEDBITS() returns in the loop. For example, want, need, and keep
|
||
would all have to actually be part of the saved state in case NEEDBITS()
|
||
returns:
|
||
|
||
case STATEw:
|
||
while (want < need) {
|
||
NEEDBITS(n);
|
||
keep[want++] = BITS(n);
|
||
DROPBITS(n);
|
||
}
|
||
state = STATEx;
|
||
case STATEx:
|
||
|
||
As shown above, if the next state is also the next case, then the break
|
||
is omitted.
|
||
|
||
A state may also return if there is not enough output space available to
|
||
complete that state. Those states are copying stored data, writing a
|
||
literal byte, and copying a matching string.
|
||
|
||
When returning, a "goto inf_leave" is used to update the total counters,
|
||
update the check value, and determine whether any progress has been made
|
||
during that inflate() call in order to return the proper return code.
|
||
Progress is defined as a change in either strm->avail_in or strm->avail_out.
|
||
When there is a window, goto inf_leave will update the window with the last
|
||
output written. If a goto inf_leave occurs in the middle of decompression
|
||
and there is no window currently, goto inf_leave will create one and copy
|
||
output to the window for the next call of inflate().
|
||
|
||
In this implementation, the flush parameter of inflate() only affects the
|
||
return code (per zlib.h). inflate() always writes as much as possible to
|
||
strm->next_out, given the space available and the provided input--the effect
|
||
documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers
|
||
the allocation of and copying into a sliding window until necessary, which
|
||
provides the effect documented in zlib.h for Z_FINISH when the entire input
|
||
stream available. So the only thing the flush parameter actually does is:
|
||
when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it
|
||
will return Z_BUF_ERROR if it has not reached the end of the stream.
|
||
*/
|
||
|
||
/* permutation of code lengths */
|
||
var inflate_order = [
|
||
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
|
||
ZLIB.inflate = function(strm, flush)
|
||
{
|
||
var state;
|
||
var s;
|
||
var _in, out; /* save starting available input and output */
|
||
var copy; /* number of stored or match bytes to copy */
|
||
var from_window_offset = -1; /* index of window[] */
|
||
var from_out_offset = -1; /* index of next_out[] */
|
||
var here; /* current decoding table entry */
|
||
var last; /* parent table entry */
|
||
var len; /* length to copy for repeats, bits to drop */
|
||
var ret; /* return code */
|
||
|
||
if (!strm || !strm.state ||
|
||
(!strm.input_data && strm.avail_in != 0))
|
||
return ZLIB.Z_STREAM_ERROR;
|
||
|
||
state = strm.state;
|
||
if (state.mode == TYPE) state.mode = TYPEDO; /* skip check */
|
||
|
||
// LOAD
|
||
s = {};
|
||
LOAD(strm, s);
|
||
|
||
_in = s.have;
|
||
out = s.left;
|
||
ret = ZLIB.Z_OK;
|
||
inf_leave: for (;;) {
|
||
switch (state.mode) {
|
||
case HEAD:
|
||
if (state.wrap == 0) {
|
||
state.mode = TYPEDO;
|
||
break;
|
||
}
|
||
if(!NEEDBITS(s, 16)) break inf_leave;
|
||
// #ifdef GUNZIP
|
||
if ((state.wrap & 2) && s.hold == 0x8b1f) { /* gzip header */
|
||
state.check = strm.checksum_function(0, null, 0, 0);
|
||
CRC2(strm, s.hold);
|
||
INITBITS(s);
|
||
state.mode = FLAGS;
|
||
break;
|
||
}
|
||
state.flags = 0; /* expect zlib header */
|
||
if (state.head !== null)
|
||
state.head.done = -1;
|
||
if (!(state.wrap & 1) || /* check if zlib header allowed */
|
||
//#else
|
||
// if (
|
||
//#endif
|
||
((BITS(s, 8) << 8) + (s.hold >>> 8)) % 31) {
|
||
strm.msg = 'incorrect header check';
|
||
state.mode = BAD;
|
||
break;
|
||
}
|
||
if (BITS(s, 4) != ZLIB.Z_DEFLATED) {
|
||
strm.msg = 'unknown compression method';
|
||
state.mode = BAD;
|
||
break;
|
||
}
|
||
|
||
DROPBITS(s, 4);
|
||
len = BITS(s, 4) + 8;
|
||
if (state.wbits == 0)
|
||
state.wbits = len;
|
||
else if (len > state.wbits) {
|
||
strm.msg = 'invalid window size';
|
||
state.mode = BAD;
|
||
break;
|
||
}
|
||
state.dmax = 1 << len;
|
||
// Tracev((stderr, "inflate: zlib header ok\n"));
|
||
strm.adler = state.check = strm.checksum_function(0, null, 0, 0);
|
||
state.mode = s.hold & 0x200 ? DICTID : TYPE;
|
||
INITBITS(s);
|
||
break;
|
||
// #ifdef GUNZIP
|
||
case FLAGS:
|
||
if(!NEEDBITS(s, 16)) break inf_leave;
|
||
state.flags = s.hold;
|
||
if ((state.flags & 0xff) != ZLIB.Z_DEFLATED) {
|
||
strm.msg = "unknown compression method";
|
||
state.mode = BAD;
|
||
break;
|
||
}
|
||
if (state.flags & 0xe000) {
|
||
strm.msg = "unknown header flags set";
|
||
state.mode = BAD;
|
||
break;
|
||
}
|
||
if (state.head !== null)
|
||
state.head.text = (s.hold >>> 8) & 1;
|
||
if (state.flags & 0x0200) {
|
||
CRC2(strm, s.hold);
|
||
}
|
||
INITBITS(s);
|
||
state.mode = TIME;
|
||
case TIME:
|
||
if(!NEEDBITS(s, 32)) break inf_leave;
|
||
if (state.head !== null)
|
||
state.head.time = s.hold;
|
||
if (state.flags & 0x0200) {
|
||
CRC4(strm, s.hold);
|
||
}
|
||
INITBITS(s);
|
||
state.mode = OS;
|
||
case OS:
|
||
if(!NEEDBITS(s, 16)) break inf_leave;
|
||
if (state.head !== null) {
|
||
state.head.xflags = s.hold & 0xff;
|
||
state.head.os = s.hold >>> 8;
|
||
}
|
||
if (state.flags & 0x0200) {
|
||
CRC2(strm, s.hold);
|
||
}
|
||
INITBITS(s);
|
||
state.mode = EXLEN;
|
||
case EXLEN:
|
||
if (state.flags & 0x0400) {
|
||
if(!NEEDBITS(s, 16)) break inf_leave;
|
||
state.length = s.hold;
|
||
if (state.head !== null) {
|
||
state.head.extra_len = s.hold;
|
||
}
|
||
if (state.flags & 0x0200) {
|
||
CRC2(strm, s.hold);
|
||
}
|
||
INITBITS(s);
|
||
state.head.extra = "";
|
||
}
|
||
else if (state.head !== null) {
|
||
state.head.extra = null;
|
||
}
|
||
state.mode = EXTRA;
|
||
case EXTRA:
|
||
if (state.flags & 0x0400) {
|
||
copy = state.length;
|
||
if (copy > s.have) copy = s.have;
|
||
if (copy) {
|
||
if (state.head !== null &&
|
||
state.head.extra !== null) {
|
||
len = state.head.extra_len - state.length;
|
||
/*
|
||
zmemcpy(state->head->extra + len, next,
|
||
len + copy > state->head->extra_max ?
|
||
state->head->extra_max - len : copy);
|
||
*/
|
||
state.head.extra += strm.input_data.substring(
|
||
s.next, s.next + (len + copy > state.head.extra_max ?
|
||
state.head.extra_max - len : copy));
|
||
|
||
}
|
||
if (state.flags & 0x0200)
|
||
state.check = strm.checksum_function(state.check, strm.input_data, s.next, copy);
|
||
s.have -= copy;
|
||
s.next += copy;
|
||
state.length -= copy;
|
||
}
|
||
if (state.length) break inf_leave;
|
||
}
|
||
state.length = 0;
|
||
state.mode = NAME;
|
||
case NAME:
|
||
if (state.flags & 0x0800) {
|
||
if (s.have == 0) break inf_leave;
|
||
if (state.head !== null && state.head.name === null) {
|
||
state.head.name = "";
|
||
}
|
||
copy = 0;
|
||
// TODO end = strm.input_data.indexOf("\0", s.next);
|
||
// TODO state.length => state.head.name.length
|
||
do {
|
||
len = strm.input_data.charAt(s.next + copy); copy++;
|
||
if(len === "\0")
|
||
break;
|
||
if (state.head !== null &&
|
||
state.length < state.head.name_max) {
|
||
state.head.name += len;
|
||
state.length++;
|
||
}
|
||
} while (copy < s.have);
|
||
if (state.flags & 0x0200) {
|
||
state.check = strm.checksum_function(state.check, strm.input_data, s.next, copy);
|
||
}
|
||
s.have -= copy;
|
||
s.next += copy;
|
||
if (len !== "\0") break inf_leave;
|
||
}
|
||
else if (state.head !== null)
|
||
state.head.name = null;
|
||
state.length = 0;
|
||
state.mode = COMMENT;
|
||
case COMMENT:
|
||
if (state.flags & 0x1000) {
|
||
if (s.have == 0) break inf_leave;
|
||
copy = 0;
|
||
if (state.head !== null && state.head.comment === null) {
|
||
state.head.comment = "";
|
||
}
|
||
// TODO end = strm.input_data.indexOf("\0", s.next);
|
||
// TODO state.length => state.head.comment.length
|
||
do {
|
||
len = strm.input_data.charAt(s.next + copy); copy++;
|
||
if(len === "\0")
|
||
break;
|
||
if (state.head !== null &&
|
||
state.length < state.head.comm_max) {
|
||
state.head.comment += len;
|
||
state.length++;
|
||
}
|
||
} while (copy < s.have);
|
||
if (state.flags & 0x0200)
|
||
state.check = strm.checksum_function(state.check, strm.input_data, s.next, copy);
|
||
s.have -= copy;
|
||
s.next += copy;
|
||
if (len !== "\0") break inf_leave;
|
||
}
|
||
else if (state.head !== null)
|
||
state.head.comment = null;
|
||
state.mode = HCRC;
|
||
case HCRC:
|
||
if (state.flags & 0x0200) {
|
||
if(!NEEDBITS(s, 16)) break inf_leave;
|
||
if (s.hold != (state.check & 0xffff)) {
|
||
strm.msg = "header crc mismatch";
|
||
state.mode = BAD;
|
||
break;
|
||
}
|
||
INITBITS(s);
|
||
}
|
||
if (state.head !== null) {
|
||
state.head.hcrc = (state.flags >>> 9) & 1;
|
||
state.head.done = 1;
|
||
}
|
||
strm.adler = state.check = strm.checksum_function(0, null, 0, 0);
|
||
state.mode = TYPE;
|
||
break;
|
||
//#endif
|
||
case DICTID:
|
||
if(!NEEDBITS(s, 32)) break inf_leave;
|
||
strm.adler = state.check = REVERSE(s.hold);
|
||
INITBITS(s);
|
||
state.mode = DICT;
|
||
case DICT:
|
||
if (state.havedict == 0) {
|
||
RESTORE(s);
|
||
return ZLIB.Z_NEED_DICT;
|
||
}
|
||
strm.adler = state.check = strm.checksum_function(0, null, 0, 0);
|
||
state.mode = TYPE;
|
||
case TYPE:
|
||
if (flush == ZLIB.Z_BLOCK || flush == ZLIB.Z_TREES) break inf_leave;
|
||
case TYPEDO:
|
||
if (state.last) {
|
||
BYTEBITS(s);
|
||
state.mode = CHECK;
|
||
break;
|
||
}
|
||
if(!NEEDBITS(s, 3)) break inf_leave;
|
||
state.last = BITS(s, 1);
|
||
DROPBITS(s, 1);
|
||
switch (BITS(s, 2)) {
|
||
case 0: /* stored block */
|
||
// Tracev((stderr, "inflate: stored block%s\n",
|
||
// state->last ? " (last)" : ""));
|
||
state.mode = STORED;
|
||
break;
|
||
case 1: /* fixed block */
|
||
fixedtables(state);
|
||
// Tracev((stderr, "inflate: fixed codes block%s\n",
|
||
// state->last ? " (last)" : ""));
|
||
state.mode = LEN_; /* decode codes */
|
||
if (flush == ZLIB.Z_TREES) {
|
||
DROPBITS(s, 2);
|
||
break inf_leave;
|
||
}
|
||
break;
|
||
case 2: /* dynamic block */
|
||
// Tracev((stderr, "inflate: dynamic codes block%s\n",
|
||
// state->last ? " (last)" : ""));
|
||
state.mode = TABLE;
|
||
break;
|
||
case 3:
|
||
strm.msg = 'invalid block type';
|
||
state.mode = BAD;
|
||
}
|
||
DROPBITS(s, 2);
|
||
break;
|
||
case STORED:
|
||
BYTEBITS(s); /* go to byte boundary */
|
||
if(!NEEDBITS(s, 32)) break inf_leave;
|
||
if ((s.hold & 0xffff) != (((s.hold >>> 16) & 0xffff) ^ 0xffff)) {
|
||
strm.msg = 'invalid stored block lengths';
|
||
state.mode = BAD;
|
||
break;
|
||
}
|
||
state.length = s.hold & 0xffff;
|
||
// Tracev((stderr, "inflate: stored length %u\n",
|
||
// state->length));
|
||
INITBITS(s);
|
||
state.mode = COPY_;
|
||
if (flush == ZLIB.Z_TREES) break inf_leave;
|
||
case COPY_:
|
||
state.mode = COPY;
|
||
case COPY:
|
||
copy = state.length;
|
||
if (copy) {
|
||
if (copy > s.have) copy = s.have;
|
||
if (copy > s.left) copy = s.left;
|
||
if (copy == 0) break inf_leave;
|
||
strm.output_data += strm.input_data.substring(s.next, s.next + copy);
|
||
strm.next_out += copy;
|
||
s.have -= copy;
|
||
s.next += copy;
|
||
s.left -= copy;
|
||
state.length -= copy;
|
||
break;
|
||
}
|
||
// Tracev((stderr, "inflate: stored end\n"));
|
||
state.mode = TYPE;
|
||
break;
|
||
case TABLE:
|
||
if(!NEEDBITS(s, 14)) break inf_leave;
|
||
state.nlen = BITS(s, 5) + 257;
|
||
DROPBITS(s, 5);
|
||
state.ndist = BITS(s, 5) + 1;
|
||
DROPBITS(s, 5);
|
||
state.ncode = BITS(s, 4) + 4;
|
||
DROPBITS(s, 4);
|
||
//#ifndef PKZIP_BUG_WORKAROUND
|
||
if (state.nlen > 286 || state.ndist > 30) {
|
||
strm.msg = 'too many length or distance symbols';
|
||
state.mode = BAD;
|
||
break;
|
||
}
|
||
//#endif
|
||
// Tracev((stderr, "inflate: table sizes ok\n"));
|
||
state.have = 0;
|
||
state.mode = LENLENS;
|
||
case LENLENS:
|
||
while (state.have < state.ncode) {
|
||
if(!NEEDBITS(s, 3)) break inf_leave;
|
||
var tmp = BITS(s, 3);
|
||
state.lens[inflate_order[state.have++]] = tmp;
|
||
DROPBITS(s, 3);
|
||
}
|
||
while (state.have < 19)
|
||
state.lens[inflate_order[state.have++]] = 0;
|
||
state.next = 0;
|
||
state.lencode = 0;
|
||
state.lenbits = 7;
|
||
|
||
// ret = inflate_table(CODES, state->lens, 19, &(state->next),
|
||
// &(state->lenbits), state->work);
|
||
ret = inflate_table(state, CODES);
|
||
|
||
if (ret) {
|
||
strm.msg = 'invalid code lengths set';
|
||
state.mode = BAD;
|
||
break;
|
||
}
|
||
// Tracev((stderr, "inflate: code lengths ok\n"));
|
||
state.have = 0;
|
||
state.mode = CODELENS;
|
||
case CODELENS:
|
||
while (state.have < state.nlen + state.ndist) {
|
||
for (;;) {
|
||
here = state.codes[state.lencode + BITS(s, state.lenbits)];
|
||
if (here.bits <= s.bits) break;
|
||
if(!PULLBYTE(s)) break inf_leave;
|
||
}
|
||
if (here.val < 16) {
|
||
DROPBITS(s, here.bits);
|
||
state.lens[state.have++] = here.val;
|
||
}
|
||
else {
|
||
if (here.val == 16) {
|
||
if(!NEEDBITS(s, here.bits + 2)) break inf_leave;
|
||
DROPBITS(s, here.bits);
|
||
if (state.have == 0) {
|
||
strm.msg = 'invalid bit length repeat';
|
||
state.mode = BAD;
|
||
break;
|
||
}
|
||
len = state.lens[state.have - 1];
|
||
copy = 3 + BITS(s, 2);
|
||
DROPBITS(s, 2);
|
||
}
|
||
else if (here.val == 17) {
|
||
if(!NEEDBITS(s, here.bits + 3)) break inf_leave;
|
||
DROPBITS(s, here.bits);
|
||
len = 0;
|
||
copy = 3 + BITS(s, 3);
|
||
DROPBITS(s, 3);
|
||
}
|
||
else {
|
||
if(!NEEDBITS(s, here.bits + 7)) break inf_leave;
|
||
DROPBITS(s, here.bits);
|
||
len = 0;
|
||
copy = 11 + BITS(s, 7);
|
||
DROPBITS(s, 7);
|
||
}
|
||
if (state.have + copy > state.nlen + state.ndist) {
|
||
strm.msg = 'invalid bit length repeat';
|
||
state.mode = BAD;
|
||
break;
|
||
}
|
||
while (copy--)
|
||
state.lens[state.have++] = len;
|
||
}
|
||
}
|
||
|
||
/* handle error breaks in while */
|
||
if (state.mode == BAD) break;
|
||
|
||
/* check for end-of-block code (better have one) */
|
||
if (state.lens[256] == 0) {
|
||
strm.msg = 'invalid code -- missing end-of-block';
|
||
state.mode = BAD;
|
||
break;
|
||
}
|
||
|
||
/* build code tables -- note: do not change the lenbits or distbits
|
||
values here (9 and 6) without reading the comments in inftrees.h
|
||
concerning the ENOUGH constants, which depend on those values */
|
||
state.next = 0;
|
||
state.lencode = state.next;
|
||
state.lenbits = 9;
|
||
// ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
|
||
// &(state->lenbits), state->work);
|
||
ret = inflate_table(state, LENS);
|
||
if (ret) {
|
||
strm.msg = 'invalid literal/lengths set';
|
||
state.mode = BAD;
|
||
break;
|
||
}
|
||
state.distcode = state.next;
|
||
state.distbits = 6;
|
||
// ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, &(state->next),
|
||
// &(state->distbits), state->work);
|
||
ret = inflate_table(state, DISTS);
|
||
if (ret) {
|
||
strm.msg = 'invalid distances set';
|
||
state.mode = BAD;
|
||
break;
|
||
}
|
||
// Tracev((stderr, "inflate: codes ok\n"));
|
||
state.mode = LEN_;
|
||
if (flush == ZLIB.Z_TREES) break inf_leave;
|
||
case LEN_:
|
||
state.mode = LEN;
|
||
case LEN:
|
||
if (s.have >= 6 && s.left >= 258) {
|
||
RESTORE(s);
|
||
inflate_fast(strm, out);
|
||
LOAD(strm, s);
|
||
if (state.mode == TYPE)
|
||
state.back = -1;
|
||
break;
|
||
}
|
||
state.back = 0;
|
||
for (;;) {
|
||
here = state.codes[state.lencode + BITS(s, state.lenbits)];
|
||
if (here.bits <= s.bits) break;
|
||
if(!PULLBYTE(s)) break inf_leave;
|
||
}
|
||
if (here.op && (here.op & 0xf0) == 0) {
|
||
last = here;
|
||
for (;;) {
|
||
here = state.codes[state.lencode + last.val +
|
||
(BITS(s, last.bits + last.op) >>> last.bits)];
|
||
if (last.bits + here.bits <= s.bits) break;
|
||
if(!PULLBYTE(s)) break inf_leave;
|
||
}
|
||
DROPBITS(s, last.bits);
|
||
state.back += last.bits;
|
||
}
|
||
DROPBITS(s, here.bits);
|
||
state.back += here.bits;
|
||
state.length = here.val;
|
||
if (here.op == 0) {
|
||
// Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
|
||
// "inflate: literal '%c'\n" :
|
||
// "inflate: literal 0x%02x\n", here.val));
|
||
state.mode = LIT;
|
||
break;
|
||
}
|
||
if (here.op & 32) {
|
||
// Tracevv((stderr, "inflate: end of block\n"));
|
||
state.back = -1;
|
||
state.mode = TYPE;
|
||
break;
|
||
}
|
||
if (here.op & 64) {
|
||
strm.msg = 'invalid literal/length code';
|
||
state.mode = BAD;
|
||
break;
|
||
}
|
||
state.extra = here.op & 15;
|
||
state.mode = LENEXT;
|
||
case LENEXT:
|
||
if (state.extra) {
|
||
if(!NEEDBITS(s, state.extra)) break inf_leave;
|
||
state.length += BITS(s, state.extra);
|
||
DROPBITS(s, state.extra);
|
||
state.back += state.extra;
|
||
}
|
||
//Tracevv((stderr, "inflate: length %u\n", state->length));
|
||
state.was = state.length;
|
||
state.mode = DIST;
|
||
case DIST:
|
||
for (;;) {
|
||
here = state.codes[state.distcode + BITS(s, state.distbits)];
|
||
if (here.bits <= s.bits) break;
|
||
if(!PULLBYTE(s)) break inf_leave;
|
||
}
|
||
if ((here.op & 0xf0) == 0) {
|
||
last = here;
|
||
for (;;) {
|
||
here = state.codes[state.distcode + last.val +
|
||
(BITS(s, last.bits + last.op) >>> last.bits)];
|
||
if ((last.bits + here.bits) <= s.bits) break;
|
||
if(!PULLBYTE(s)) break inf_leave;
|
||
}
|
||
DROPBITS(s, last.bits);
|
||
state.back += last.bits;
|
||
}
|
||
DROPBITS(s, here.bits);
|
||
state.back += here.bits;
|
||
if (here.op & 64) {
|
||
strm.msg = 'invalid distance code';
|
||
state.mode = BAD;
|
||
break;
|
||
}
|
||
state.offset = here.val;
|
||
state.extra = here.op & 15;
|
||
state.mode = DISTEXT;
|
||
case DISTEXT:
|
||
if (state.extra) {
|
||
if(!NEEDBITS(s, state.extra)) break inf_leave;
|
||
state.offset += BITS(s, state.extra);
|
||
DROPBITS(s, state.extra);
|
||
state.back += state.extra;
|
||
}
|
||
//NOSPRT #ifdef INFLATE_STRICT
|
||
// if (state->offset > state->dmax) {
|
||
// strm->msg = (char *)"invalid distance too far back";
|
||
// state->mode = BAD;
|
||
// break;
|
||
// }
|
||
//#endif
|
||
// Tracevv((stderr, "inflate: distance %u\n", state->offset));
|
||
state.mode = MATCH;
|
||
case MATCH:
|
||
if (s.left == 0) break inf_leave;
|
||
copy = out - s.left;
|
||
if (state.offset > copy) { /* copy from window */
|
||
copy = state.offset - copy;
|
||
if (copy > state.whave) {
|
||
if (state.sane) {
|
||
strm.msg = 'invalid distance too far back';
|
||
state.mode = BAD;
|
||
break;
|
||
}
|
||
//NOSPRT #ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
|
||
// Trace((stderr, "inflate.c too far\n"));
|
||
// copy -= state->whave;
|
||
// if (copy > state->length) copy = state->length;
|
||
// if (copy > left) copy = left;
|
||
// left -= copy;
|
||
// state->length -= copy;
|
||
// do {
|
||
// *put++ = 0;
|
||
// } while (--copy);
|
||
// if (state->length == 0) state->mode = LEN;
|
||
// break;
|
||
//#endif
|
||
}
|
||
if (copy > state.wnext) {
|
||
copy -= state.wnext;
|
||
// from = state->window + (state->wsize - copy);
|
||
from_window_offset = state.wsize - copy;
|
||
from_out_offset = -1;
|
||
}
|
||
else {
|
||
// from = state->window + (state->wnext - copy);
|
||
from_window_offset = state.wnext - copy;
|
||
from_out_offset = -1;
|
||
}
|
||
if (copy > state.length) copy = state.length;
|
||
}
|
||
else { /* copy from output */
|
||
// from = put - state->offset;
|
||
from_window_offset = -1;
|
||
from_out_offset = strm.next_out - state.offset;
|
||
copy = state.length;
|
||
}
|
||
if (copy > s.left) copy = s.left;
|
||
s.left -= copy;
|
||
state.length -= copy;
|
||
if( from_window_offset >= 0 ) {
|
||
strm.output_data += state.window.substring(from_window_offset, from_window_offset + copy);
|
||
strm.next_out += copy;
|
||
copy = 0;
|
||
} else {
|
||
strm.next_out += copy;
|
||
do {
|
||
strm.output_data += strm.output_data.charAt(from_out_offset++);
|
||
} while (--copy);
|
||
}
|
||
if (state.length == 0) state.mode = LEN;
|
||
break;
|
||
case LIT:
|
||
if (s.left == 0) break inf_leave;
|
||
|
||
strm.output_data += String.fromCharCode(state.length);
|
||
strm.next_out++;
|
||
//*put++ = (unsigned char)(state->length);
|
||
|
||
s.left--;
|
||
state.mode = LEN;
|
||
break;
|
||
case CHECK:
|
||
if (state.wrap) {
|
||
if(!NEEDBITS(s, 32)) break inf_leave;
|
||
out -= s.left;
|
||
strm.total_out += out;
|
||
state.total += out;
|
||
if (out)
|
||
strm.adler = state.check =
|
||
strm.checksum_function(state.check, strm.output_data, strm.output_data.length - out, out);
|
||
out = s.left;
|
||
if ((
|
||
// #ifdef GUNZIP
|
||
state.flags ? s.hold :
|
||
//#endif
|
||
REVERSE(s.hold)) != state.check) {
|
||
strm.msg = "incorrect data check";
|
||
state.mode = BAD;
|
||
break;
|
||
}
|
||
INITBITS(s);
|
||
//debug("## inflate: check matches trailer\n");
|
||
// Tracev((stderr, "inflate: check matches trailer\n"));
|
||
}
|
||
//#ifdef GUNZIP
|
||
state.mode = LENGTH;
|
||
case LENGTH:
|
||
if (state.wrap && state.flags) {
|
||
if(!NEEDBITS(s, 32)) break inf_leave;
|
||
if (s.hold != (state.total & 0xffffffff)) {
|
||
strm.msg = 'incorrect length check';
|
||
state.mode = BAD;
|
||
break;
|
||
}
|
||
INITBITS(s);
|
||
//Tracev((stderr, "inflate: length matches trailer\n"));
|
||
}
|
||
//#endif
|
||
state.mode = DONE;
|
||
case DONE:
|
||
ret = ZLIB.Z_STREAM_END;
|
||
break inf_leave;
|
||
case BAD:
|
||
ret = ZLIB.Z_DATA_ERROR;
|
||
break inf_leave;
|
||
case MEM:
|
||
return ZLIB.Z_MEM_ERROR;
|
||
case SYNC:
|
||
default:
|
||
return ZLIB.Z_STREAM_ERROR;
|
||
} }
|
||
|
||
/*
|
||
Return from inflate(), updating the total counts and the check value.
|
||
If there was no progress during the inflate() call, return a buffer
|
||
error. Call updatewindow() to create and/or update the window state.
|
||
Note: a memory error from inflate() is non-recoverable.
|
||
*/
|
||
inf_leave:
|
||
RESTORE(s);
|
||
if (state.wsize || (out != strm.avail_out && state.mode < BAD &&
|
||
(state.mode < CHECK || flush != ZLIB.Z_FINISH)))
|
||
if (updatewindow(strm)) {
|
||
state.mode = MEM;
|
||
return ZLIB.Z_MEM_ERROR;
|
||
}
|
||
_in -= strm.avail_in;
|
||
out -= strm.avail_out;
|
||
strm.total_in += _in;
|
||
strm.total_out += out;
|
||
state.total += out;
|
||
if (state.wrap && out)
|
||
strm.adler = state.check = strm.checksum_function(state.check, strm.output_data, 0, strm.output_data.length);
|
||
strm.data_type = state.bits + (state.last ? 64 : 0) +
|
||
(state.mode == TYPE ? 128 : 0) +
|
||
(state.mode == LEN_ || state.mode == COPY_ ? 256 : 0);
|
||
if (((_in == 0 && out == 0) || flush == ZLIB.Z_FINISH) && ret == ZLIB.Z_OK)
|
||
ret = ZLIB.Z_BUF_ERROR;
|
||
return ret;
|
||
};
|
||
|
||
ZLIB.inflateEnd = function(strm)
|
||
{
|
||
var state;
|
||
if (!strm || !strm.state )
|
||
return ZLIB.Z_STREAM_ERROR;
|
||
state = strm.state;
|
||
state.window = null;
|
||
strm.state = null;
|
||
// Tracev((stderr, "inflate: end\n"));
|
||
return ZLIB.Z_OK;
|
||
};
|
||
|
||
ZLIB.z_stream.prototype.inflate = function(input_string, opts)
|
||
{
|
||
var flush;
|
||
var avail_out;
|
||
var DEFAULT_BUFFER_SIZE = 16384;
|
||
|
||
this.input_data = input_string;
|
||
this.next_in = getarg(opts, 'next_in', 0);
|
||
this.avail_in = getarg(opts, 'avail_in', input_string.length - this.next_in);
|
||
|
||
flush = getarg(opts, 'flush', ZLIB.Z_SYNC_FLUSH);
|
||
avail_out = getarg(opts, 'avail_out', -1);
|
||
|
||
var result = '';
|
||
do {
|
||
this.avail_out = (avail_out >= 0 ? avail_out : DEFAULT_BUFFER_SIZE);
|
||
this.output_data = '';
|
||
this.next_out = 0;
|
||
this.error = ZLIB.inflate(this, flush);
|
||
if(avail_out >= 0) {
|
||
return this.output_data;
|
||
}
|
||
result += this.output_data;
|
||
if(this.avail_out > 0) {
|
||
break;
|
||
}
|
||
} while(this.error == ZLIB.Z_OK);
|
||
|
||
return result;
|
||
};
|
||
|
||
ZLIB.z_stream.prototype.inflateReset = function(windowBits)
|
||
{
|
||
return ZLIB.inflateReset(this, windowBits);
|
||
};
|
||
|
||
}());
|
||
/* zlib-adler32.js -- JavaScript implementation for the zlib adler32.
|
||
Version: 0.2.0
|
||
LastModified: Apr 12 2012
|
||
Copyright (C) 2012 Masanao Izumo <iz@onicos.co.jp>
|
||
|
||
API documentation
|
||
==============================================================================
|
||
Usage: adler = ZLIB.adler32(adler, buf, offset, len);
|
||
|
||
Update a running Adler-32 checksum with the bytes buf[offset..offset+len-1] and
|
||
return the updated checksum. If buf is null, this function returns the
|
||
required initial value for the checksum.
|
||
|
||
An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
|
||
much faster.
|
||
|
||
Usage example:
|
||
|
||
var adler = ZLIB.adler32(0, null, 0, 0);
|
||
|
||
while (read_buffer(buffer, length) != EOF) {
|
||
adler = ZLIB.adler32(adler, buffer, 0, length);
|
||
}
|
||
if (adler != original_adler) error();
|
||
|
||
==============================================================================
|
||
Usage: adler = ZLIB.adler32_combine(adler1, adler2, len2);
|
||
|
||
Combine two Adler-32 checksums into one. For two sequences of bytes, seq1
|
||
and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for
|
||
each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of
|
||
seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note
|
||
that the z_off_t type (like off_t) is a signed integer. If len2 is
|
||
negative, the result has no meaning or utility.
|
||
*/
|
||
|
||
if( typeof ZLIB === 'undefined' ) {
|
||
alert('ZLIB is not defined. SRC zlib.js before zlib-adler32.js')
|
||
}
|
||
|
||
(function() {
|
||
|
||
/* adler32.c -- compute the Adler-32 checksum of a data stream
|
||
* Copyright (C) 1995-2011 Mark Adler
|
||
* For conditions of distribution and use, see copyright notice in zlib.h
|
||
*/
|
||
|
||
var BASE = 65521; /* largest prime smaller than 65536 */
|
||
var NMAX = 5552;
|
||
/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
|
||
|
||
/* ========================================================================= */
|
||
function adler32_string(adler, buf, offset, len)
|
||
{
|
||
var sum2;
|
||
var n;
|
||
|
||
/* split Adler-32 into component sums */
|
||
sum2 = (adler >>> 16) & 0xffff;
|
||
adler &= 0xffff;
|
||
|
||
/* in case user likes doing a byte at a time, keep it fast */
|
||
if (len == 1) {
|
||
adler += buf.charCodeAt(offset) & 0xff;
|
||
if (adler >= BASE)
|
||
adler -= BASE;
|
||
sum2 += adler;
|
||
if (sum2 >= BASE)
|
||
sum2 -= BASE;
|
||
return adler | (sum2 << 16);
|
||
}
|
||
|
||
/* initial Adler-32 value (deferred check for len == 1 speed) */
|
||
if (buf === null)
|
||
return 1;
|
||
|
||
/* in case short lengths are provided, keep it somewhat fast */
|
||
if (len < 16) {
|
||
while (len--) {
|
||
adler += buf.charCodeAt(offset++) & 0xff;
|
||
sum2 += adler;
|
||
}
|
||
if (adler >= BASE)
|
||
adler -= BASE;
|
||
sum2 %= BASE; /* only added so many BASE's */
|
||
return adler | (sum2 << 16);
|
||
}
|
||
|
||
/* do length NMAX blocks -- requires just one modulo operation */
|
||
while (len >= NMAX) {
|
||
len -= NMAX;
|
||
n = NMAX >> 4; /* NMAX is divisible by 16 */
|
||
do {
|
||
/* 16 sums unrolled */
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
} while (--n);
|
||
adler %= BASE;
|
||
sum2 %= BASE;
|
||
}
|
||
|
||
/* do remaining bytes (less than NMAX, still just one modulo) */
|
||
if (len) { /* avoid modulos if none remaining */
|
||
while (len >= 16) {
|
||
len -= 16;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
}
|
||
while (len--) {
|
||
adler += buf.charCodeAt(offset++) & 0xff; sum2 += adler;
|
||
}
|
||
adler %= BASE;
|
||
sum2 %= BASE;
|
||
}
|
||
|
||
/* return recombined sums */
|
||
return adler | (sum2 << 16);
|
||
}
|
||
|
||
/* ========================================================================= */
|
||
function adler32_array(adler, buf, offset, len)
|
||
{
|
||
var sum2;
|
||
var n;
|
||
|
||
/* split Adler-32 into component sums */
|
||
sum2 = (adler >>> 16) & 0xffff;
|
||
adler &= 0xffff;
|
||
|
||
/* in case user likes doing a byte at a time, keep it fast */
|
||
if (len == 1) {
|
||
adler += buf[offset];
|
||
if (adler >= BASE)
|
||
adler -= BASE;
|
||
sum2 += adler;
|
||
if (sum2 >= BASE)
|
||
sum2 -= BASE;
|
||
return adler | (sum2 << 16);
|
||
}
|
||
|
||
/* initial Adler-32 value (deferred check for len == 1 speed) */
|
||
if (buf === null)
|
||
return 1;
|
||
|
||
/* in case short lengths are provided, keep it somewhat fast */
|
||
if (len < 16) {
|
||
while (len--) {
|
||
adler += buf[offset++];
|
||
sum2 += adler;
|
||
}
|
||
if (adler >= BASE)
|
||
adler -= BASE;
|
||
sum2 %= BASE; /* only added so many BASE's */
|
||
return adler | (sum2 << 16);
|
||
}
|
||
|
||
/* do length NMAX blocks -- requires just one modulo operation */
|
||
while (len >= NMAX) {
|
||
len -= NMAX;
|
||
n = NMAX >> 4; /* NMAX is divisible by 16 */
|
||
do {
|
||
/* 16 sums unrolled */
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
} while (--n);
|
||
adler %= BASE;
|
||
sum2 %= BASE;
|
||
}
|
||
|
||
/* do remaining bytes (less than NMAX, still just one modulo) */
|
||
if (len) { /* avoid modulos if none remaining */
|
||
while (len >= 16) {
|
||
len -= 16;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
adler += buf[offset++]; sum2 += adler;
|
||
}
|
||
while (len--) {
|
||
adler += buf[offset++]; sum2 += adler;
|
||
}
|
||
adler %= BASE;
|
||
sum2 %= BASE;
|
||
}
|
||
|
||
/* return recombined sums */
|
||
return adler | (sum2 << 16);
|
||
}
|
||
|
||
/* ========================================================================= */
|
||
ZLIB.adler32 = function(adler, buf, offset, len)
|
||
{
|
||
if(typeof buf === 'string') {
|
||
return adler32_string(adler, buf, offset, len);
|
||
} else {
|
||
return adler32_array(adler, buf, offset, len);
|
||
}
|
||
};
|
||
|
||
ZLIB.adler32_combine = function(adler1, adler2, len2)
|
||
{
|
||
var sum1;
|
||
var sum2;
|
||
var rem;
|
||
|
||
/* for negative len, return invalid adler32 as a clue for debugging */
|
||
if (len2 < 0)
|
||
return 0xffffffff;
|
||
|
||
/* the derivation of this formula is left as an exercise for the reader */
|
||
len2 %= BASE; /* assumes len2 >= 0 */
|
||
rem = len2;
|
||
sum1 = adler1 & 0xffff;
|
||
sum2 = rem * sum1;
|
||
sum2 %= BASE;
|
||
sum1 += (adler2 & 0xffff) + BASE - 1;
|
||
sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem;
|
||
if (sum1 >= BASE) sum1 -= BASE;
|
||
if (sum1 >= BASE) sum1 -= BASE;
|
||
if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1);
|
||
if (sum2 >= BASE) sum2 -= BASE;
|
||
return sum1 | (sum2 << 16);
|
||
}
|
||
|
||
}());
|
||
/* zlib-adler32.js -- JavaScript implementation for the zlib crc32.
|
||
Version: 0.2.0
|
||
LastModified: Apr 12 2012
|
||
Copyright (C) 2012 Masanao Izumo <iz@onicos.co.jp>
|
||
|
||
API documentation
|
||
==============================================================================
|
||
Usage: crc = ZLIB.crc32(crc, buf, offset, len);
|
||
|
||
Update a running CRC-32 with the bytes buf[offset..offset+len-1] and return the
|
||
updated CRC-32. If buf is null, this function returns the required
|
||
initial value for the for the crc. Pre- and post-conditioning (one's
|
||
complement) is performed within this function so it shouldn't be done by the
|
||
application.
|
||
|
||
Usage example:
|
||
|
||
var crc = ZLIB.crc32(0, null, 0, 0);
|
||
|
||
while (read_buffer(buffer, length) != EOF) {
|
||
crc = ZLIB.crc32(crc, buffer, 0, length);
|
||
}
|
||
if (crc != original_crc) error();
|
||
|
||
==============================================================================
|
||
Usage: crc = crc32_combine(crc1, crc2, len2);
|
||
|
||
Combine two CRC-32 check values into one. For two sequences of bytes,
|
||
seq1 and seq2 with lengths len1 and len2, CRC-32 check values were
|
||
calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32
|
||
check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and
|
||
len2.
|
||
*/
|
||
|
||
if( typeof ZLIB === 'undefined' ) {
|
||
alert('ZLIB is not defined. SRC zlib.js before zlib-crc32.js')
|
||
}
|
||
|
||
(function() {
|
||
|
||
/* crc32.c -- compute the CRC-32 of a data stream
|
||
* Copyright (C) 1995-2006, 2010, 2011 Mark Adler
|
||
* For conditions of distribution and use, see copyright notice in zlib.h
|
||
*
|
||
* Thanks to Rodney Brown <rbrown64@csc.com.au> for his contribution of faster
|
||
* CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing
|
||
* tables for updating the shift register in one step with three exclusive-ors
|
||
* instead of four steps with four exclusive-ors. This results in about a
|
||
* factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3.
|
||
*/
|
||
|
||
var crc_table = [
|
||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
|
||
0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
|
||
0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
|
||
0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
|
||
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
|
||
0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
||
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
|
||
0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
|
||
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
|
||
0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
|
||
0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
|
||
0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
|
||
0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
|
||
0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
|
||
0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
|
||
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
|
||
0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
||
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
|
||
0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
|
||
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
|
||
0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
|
||
0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
|
||
0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
|
||
0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
|
||
0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
|
||
0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
|
||
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
|
||
0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
||
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
|
||
0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
|
||
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
|
||
0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
|
||
0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
|
||
0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
|
||
0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
|
||
0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
|
||
0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
|
||
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
|
||
0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
||
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
|
||
0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
|
||
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
|
||
0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
|
||
0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
|
||
0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
|
||
0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
|
||
0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
|
||
0x2d02ef8d ];
|
||
|
||
/* ========================================================================= */
|
||
function crc32_string(crc, buf, offset, len)
|
||
{
|
||
if (buf == null) return 0;
|
||
|
||
crc = crc ^ 0xffffffff;
|
||
while (len >= 8) {
|
||
crc = crc_table[(crc ^ buf.charCodeAt(offset++)) & 0xff] ^ (crc >>> 8)
|
||
crc = crc_table[(crc ^ buf.charCodeAt(offset++)) & 0xff] ^ (crc >>> 8)
|
||
crc = crc_table[(crc ^ buf.charCodeAt(offset++)) & 0xff] ^ (crc >>> 8)
|
||
crc = crc_table[(crc ^ buf.charCodeAt(offset++)) & 0xff] ^ (crc >>> 8)
|
||
crc = crc_table[(crc ^ buf.charCodeAt(offset++)) & 0xff] ^ (crc >>> 8)
|
||
crc = crc_table[(crc ^ buf.charCodeAt(offset++)) & 0xff] ^ (crc >>> 8)
|
||
crc = crc_table[(crc ^ buf.charCodeAt(offset++)) & 0xff] ^ (crc >>> 8)
|
||
crc = crc_table[(crc ^ buf.charCodeAt(offset++)) & 0xff] ^ (crc >>> 8)
|
||
len -= 8;
|
||
}
|
||
if (len) do {
|
||
crc = crc_table[(crc ^ buf.charCodeAt(offset++)) & 0xff] ^ (crc >>> 8)
|
||
} while (--len);
|
||
return crc ^ 0xffffffff;
|
||
}
|
||
|
||
/* ========================================================================= */
|
||
function crc32_array(crc, buf, offset, len)
|
||
{
|
||
if (buf == null) return 0;
|
||
|
||
crc = crc ^ 0xffffffff;
|
||
while (len >= 8) {
|
||
crc = crc_table[(crc ^ buf[offset++]) & 0xff] ^ (crc >>> 8)
|
||
crc = crc_table[(crc ^ buf[offset++]) & 0xff] ^ (crc >>> 8)
|
||
crc = crc_table[(crc ^ buf[offset++]) & 0xff] ^ (crc >>> 8)
|
||
crc = crc_table[(crc ^ buf[offset++]) & 0xff] ^ (crc >>> 8)
|
||
crc = crc_table[(crc ^ buf[offset++]) & 0xff] ^ (crc >>> 8)
|
||
crc = crc_table[(crc ^ buf[offset++]) & 0xff] ^ (crc >>> 8)
|
||
crc = crc_table[(crc ^ buf[offset++]) & 0xff] ^ (crc >>> 8)
|
||
crc = crc_table[(crc ^ buf[offset++]) & 0xff] ^ (crc >>> 8)
|
||
len -= 8;
|
||
}
|
||
if (len) do {
|
||
crc = crc_table[(crc ^ buf[offset++]) & 0xff] ^ (crc >>> 8)
|
||
} while (--len);
|
||
return crc ^ 0xffffffff;
|
||
}
|
||
|
||
/* ========================================================================= */
|
||
ZLIB.crc32 = function(crc, buf, offset, len)
|
||
{
|
||
if(typeof buf === 'string') {
|
||
return crc32_string(crc, buf, offset, len);
|
||
} else {
|
||
return crc32_array(crc, buf, offset, len);
|
||
}
|
||
};
|
||
|
||
/* ========================================================================= */
|
||
var GF2_DIM = 32; /* dimension of GF(2) vectors (length of CRC) */
|
||
|
||
/* ========================================================================= */
|
||
function gf2_matrix_times(mat, vec)
|
||
{
|
||
var sum;
|
||
var mat_i = 0;
|
||
|
||
sum = 0;
|
||
while (vec) {
|
||
if (vec & 1)
|
||
sum ^= mat[mat_i];
|
||
vec >>= 1;
|
||
mat_i++;
|
||
}
|
||
return sum;
|
||
}
|
||
|
||
/* ========================================================================= */
|
||
function gf2_matrix_square(square, mat)
|
||
{
|
||
var n;
|
||
|
||
for (n = 0; n < GF2_DIM; n++)
|
||
square[n] = gf2_matrix_times(mat, mat[n]);
|
||
}
|
||
|
||
/* ========================================================================= */
|
||
ZLIB.crc32_combine = function(crc1, crc2, len2)
|
||
{
|
||
var n;
|
||
var row;
|
||
var even; /* even-power-of-two zeros operator */
|
||
var odd; /* odd-power-of-two zeros operator */
|
||
|
||
/* degenerate case (also disallow negative lengths) */
|
||
if (len2 <= 0)
|
||
return crc1;
|
||
|
||
even = new Array(GF2_DIM);
|
||
odd = new Array(GF2_DIM);
|
||
|
||
/* put operator for one zero bit in odd */
|
||
odd[0] = 0xedb88320; /* CRC-32 polynomial */
|
||
row = 1;
|
||
for (n = 1; n < GF2_DIM; n++) {
|
||
odd[n] = row;
|
||
row <<= 1;
|
||
}
|
||
|
||
/* put operator for two zero bits in even */
|
||
gf2_matrix_square(even, odd);
|
||
|
||
/* put operator for four zero bits in odd */
|
||
gf2_matrix_square(odd, even);
|
||
|
||
/* apply len2 zeros to crc1 (first square will put the operator for one
|
||
zero byte, eight zero bits, in even) */
|
||
do {
|
||
/* apply zeros operator for this bit of len2 */
|
||
gf2_matrix_square(even, odd);
|
||
if (len2 & 1)
|
||
crc1 = gf2_matrix_times(even, crc1);
|
||
len2 >>= 1;
|
||
|
||
/* if no more bits set, then done */
|
||
if (len2 == 0)
|
||
break;
|
||
|
||
/* another iteration of the loop with odd and even swapped */
|
||
gf2_matrix_square(odd, even);
|
||
if (len2 & 1)
|
||
crc1 = gf2_matrix_times(odd, crc1);
|
||
len2 >>= 1;
|
||
|
||
/* if no more bits set, then done */
|
||
} while (len2 != 0);
|
||
|
||
/* return combined crc */
|
||
crc1 ^= crc2;
|
||
return crc1;
|
||
};
|
||
|
||
}());
|
||
/**
|
||
* @description Intel AMT Redirection Transport Module - using websocket relay
|
||
* @author Ylian Saint-Hilaire
|
||
* @version v0.0.1f
|
||
*/
|
||
|
||
// Construct a MeshServer object
|
||
var CreateAmtRedirect = function (module, authCookie) {
|
||
var obj = {};
|
||
obj.m = module; // This is the inner module (Terminal or Desktop)
|
||
module.parent = obj;
|
||
obj.authCookie = authCookie;
|
||
obj.State = 0;
|
||
obj.socket = null;
|
||
// ###BEGIN###{!Mode-Firmware}
|
||
obj.host = null;
|
||
obj.port = 0;
|
||
obj.user = null;
|
||
obj.pass = null;
|
||
obj.authuri = "/RedirectionService";
|
||
obj.tlsv1only = 0;
|
||
obj.inDataCount = 0;
|
||
// ###END###{!Mode-Firmware}
|
||
obj.connectstate = 0;
|
||
obj.protocol = module.protocol; // 1 = SOL, 2 = KVM, 3 = IDER
|
||
obj.debugmode = 0;
|
||
|
||
obj.amtaccumulator = "";
|
||
obj.amtsequence = 1;
|
||
obj.amtkeepalivetimer = null;
|
||
|
||
obj.onStateChanged = null;
|
||
|
||
// Private method
|
||
//obj.Debug = function (msg) { console.log(msg); }
|
||
|
||
obj.Start = function (host, port, user, pass, tls) {
|
||
obj.host = host;
|
||
obj.port = port;
|
||
obj.user = user;
|
||
obj.pass = pass;
|
||
obj.connectstate = 0;
|
||
obj.inDataCount = 0;
|
||
var url = window.location.protocol.replace("http", "ws") + "//" + window.location.host + window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/')) + "/webrelay.ashx?p=2&host=" + host + "&port=" + port + "&tls=" + tls + ((user == '*') ? "&serverauth=1" : "") + ((typeof pass === "undefined") ? ("&serverauth=1&user=" + user) : ""); // The "p=2" indicates to the relay that this is a REDIRECTION session
|
||
if ((authCookie != null) && (authCookie != '')) { url += '&auth=' + authCookie; }
|
||
obj.socket = new WebSocket(url);
|
||
obj.socket.onopen = obj.xxOnSocketConnected;
|
||
obj.socket.onmessage = obj.xxOnMessage;
|
||
obj.socket.onclose = obj.xxOnSocketClosed;
|
||
obj.xxStateChange(1);
|
||
}
|
||
|
||
obj.xxOnSocketConnected = function () {
|
||
//obj.Debug("Redir Socket Connected");
|
||
if (obj.debugmode == 1) { console.log('onSocketConnected'); }
|
||
obj.xxStateChange(2);
|
||
if (obj.protocol == 1) obj.xxSend(obj.RedirectStartSol); // TODO: Put these strings in higher level module to tighten code
|
||
if (obj.protocol == 2) obj.xxSend(obj.RedirectStartKvm); // Don't need these is the feature is not compiled-in.
|
||
if (obj.protocol == 3) obj.xxSend(obj.RedirectStartIder);
|
||
}
|
||
|
||
// Setup the file reader
|
||
var fileReader = new FileReader();
|
||
var fileReaderInuse = false, fileReaderAcc = [];
|
||
if (fileReader.readAsBinaryString) {
|
||
// Chrome & Firefox (Draft)
|
||
fileReader.onload = function (e) { obj.xxOnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsBinaryString(new Blob([fileReaderAcc.shift()])); } }
|
||
} else if (fileReader.readAsArrayBuffer) {
|
||
// Chrome & Firefox (Spec)
|
||
fileReader.onloadend = function (e) { obj.xxOnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsArrayBuffer(fileReaderAcc.shift()); } }
|
||
}
|
||
|
||
obj.xxOnMessage = function (e) {
|
||
//if (obj.debugmode == 1) { console.log('Recv', e.data); }
|
||
obj.inDataCount++;
|
||
if (typeof e.data == 'object') {
|
||
if (fileReaderInuse == true) { fileReaderAcc.push(e.data); return; }
|
||
if (fileReader.readAsBinaryString) {
|
||
// Chrome & Firefox (Draft)
|
||
fileReaderInuse = true;
|
||
fileReader.readAsBinaryString(new Blob([e.data]));
|
||
} else if (f.readAsArrayBuffer) {
|
||
// Chrome & Firefox (Spec)
|
||
fileReaderInuse = true;
|
||
fileReader.readAsArrayBuffer(e.data);
|
||
} else {
|
||
// IE10, readAsBinaryString does not exist, use an alternative.
|
||
var binary = "", bytes = new Uint8Array(e.data), length = bytes.byteLength;
|
||
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
|
||
obj.xxOnSocketData(binary);
|
||
}
|
||
} else {
|
||
// If we get a string object, it maybe the WebRTC confirm. Ignore it.
|
||
// obj.debug("MeshDataChannel - OnData - " + typeof e.data + " - " + e.data.length);
|
||
obj.xxOnSocketData(e.data);
|
||
}
|
||
};
|
||
|
||
obj.xxOnSocketData = function (data) {
|
||
if (!data || obj.connectstate == -1) return;
|
||
|
||
if (typeof data === 'object') {
|
||
// This is an ArrayBuffer, convert it to a string array (used in IE)
|
||
var binary = "";
|
||
var bytes = new Uint8Array(data);
|
||
var length = bytes.byteLength;
|
||
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
|
||
data = binary;
|
||
}
|
||
else if (typeof data !== 'string') { return; }
|
||
|
||
if ((obj.protocol == 2 || obj.protocol == 3) && obj.connectstate == 1) { return obj.m.ProcessData(data); } // KVM traffic, forward it directly.
|
||
obj.amtaccumulator += data;
|
||
//obj.Debug("Redir Recv(" + obj.amtaccumulator.length + "): " + rstr2hex(obj.amtaccumulator));
|
||
while (obj.amtaccumulator.length >= 1) {
|
||
var cmdsize = 0;
|
||
switch (obj.amtaccumulator.charCodeAt(0)) {
|
||
case 0x11: // StartRedirectionSessionReply (17)
|
||
if (obj.amtaccumulator.length < 4) return;
|
||
var statuscode = obj.amtaccumulator.charCodeAt(1);
|
||
switch (statuscode) {
|
||
case 0: // STATUS_SUCCESS
|
||
if (obj.amtaccumulator.length < 13) return;
|
||
var oemlen = obj.amtaccumulator.charCodeAt(12);
|
||
if (obj.amtaccumulator.length < 13 + oemlen) return;
|
||
// Query for available authentication
|
||
obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)); // Query authentication support
|
||
cmdsize = (13 + oemlen);
|
||
break;
|
||
default:
|
||
obj.Stop(1);
|
||
break;
|
||
}
|
||
break;
|
||
case 0x14: // AuthenticateSessionReply (20)
|
||
if (obj.amtaccumulator.length < 9) return;
|
||
var authDataLen = ReadIntX(obj.amtaccumulator, 5);
|
||
if (obj.amtaccumulator.length < 9 + authDataLen) return;
|
||
var status = obj.amtaccumulator.charCodeAt(1);
|
||
var authType = obj.amtaccumulator.charCodeAt(4);
|
||
var authData = [];
|
||
for (i = 0; i < authDataLen; i++) { authData.push(obj.amtaccumulator.charCodeAt(9 + i)); }
|
||
var authDataBuf = obj.amtaccumulator.substring(9, 9 + authDataLen);
|
||
cmdsize = 9 + authDataLen;
|
||
if (authType == 0) {
|
||
// Query
|
||
if (authData.indexOf(4) >= 0) {
|
||
// Good Digest Auth (With cnonce and all)
|
||
obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x04) + IntToStrX(obj.user.length + obj.authuri.length + 8) + String.fromCharCode(obj.user.length) + obj.user + String.fromCharCode(0x00, 0x00) + String.fromCharCode(obj.authuri.length) + obj.authuri + String.fromCharCode(0x00, 0x00, 0x00, 0x00));
|
||
}
|
||
else if (authData.indexOf(3) >= 0) {
|
||
// Bad Digest Auth (Not sure why this is supported, cnonce is not used!)
|
||
obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x03) + IntToStrX(obj.user.length + obj.authuri.length + 7) + String.fromCharCode(obj.user.length) + obj.user + String.fromCharCode(0x00, 0x00) + String.fromCharCode(obj.authuri.length) + obj.authuri + String.fromCharCode(0x00, 0x00, 0x00));
|
||
}
|
||
else if (authData.indexOf(1) >= 0) {
|
||
// Basic Auth (Probably a good idea to not support this unless this is an old version of Intel AMT)
|
||
obj.xxSend(String.fromCharCode(0x13, 0x00, 0x00, 0x00, 0x01) + IntToStrX(obj.user.length + obj.pass.length + 2) + String.fromCharCode(obj.user.length) + obj.user + String.fromCharCode(obj.pass.length) + obj.pass);
|
||
}
|
||
else obj.Stop(2);
|
||
}
|
||
else if ((authType == 3 || authType == 4) && status == 1) {
|
||
var curptr = 0;
|
||
|
||
// Realm
|
||
var realmlen = authDataBuf.charCodeAt(curptr);
|
||
var realm = authDataBuf.substring(curptr + 1, curptr + 1 + realmlen);
|
||
curptr += (realmlen + 1);
|
||
|
||
// Nonce
|
||
var noncelen = authDataBuf.charCodeAt(curptr);
|
||
var nonce = authDataBuf.substring(curptr + 1, curptr + 1 + noncelen);
|
||
curptr += (noncelen + 1);
|
||
|
||
// QOP
|
||
var qoplen = 0;
|
||
var qop = null;
|
||
var cnonce = obj.xxRandomNonce(32);
|
||
var snc = '00000002';
|
||
var extra = '';
|
||
if (authType == 4) {
|
||
qoplen = authDataBuf.charCodeAt(curptr);
|
||
qop = authDataBuf.substring(curptr + 1, curptr + 1 + qoplen);
|
||
curptr += (qoplen + 1);
|
||
extra = snc + ":" + cnonce + ":" + qop + ":";
|
||
}
|
||
|
||
var digest = hex_md5(hex_md5(obj.user + ":" + realm + ":" + obj.pass) + ":" + nonce + ":" + extra + hex_md5("POST:" + obj.authuri));
|
||
var totallen = obj.user.length + realm.length + nonce.length + obj.authuri.length + cnonce.length + snc.length + digest.length + 7;
|
||
if (authType == 4) totallen += (qop.length + 1);
|
||
var buf = String.fromCharCode(0x13, 0x00, 0x00, 0x00, authType) + IntToStrX(totallen) + String.fromCharCode(obj.user.length) + obj.user + String.fromCharCode(realm.length) + realm + String.fromCharCode(nonce.length) + nonce + String.fromCharCode(obj.authuri.length) + obj.authuri + String.fromCharCode(cnonce.length) + cnonce + String.fromCharCode(snc.length) + snc + String.fromCharCode(digest.length) + digest;
|
||
if (authType == 4) buf += (String.fromCharCode(qop.length) + qop);
|
||
obj.xxSend(buf);
|
||
}
|
||
else
|
||
if (status == 0) { // Success
|
||
if (obj.protocol == 1) {
|
||
// Serial-over-LAN: Send Intel AMT serial settings...
|
||
var MaxTxBuffer = 10000;
|
||
var TxTimeout = 100;
|
||
var TxOverflowTimeout = 0;
|
||
var RxTimeout = 10000;
|
||
var RxFlushTimeout = 100;
|
||
var Heartbeat = 0;//5000;
|
||
obj.xxSend(String.fromCharCode(0x20, 0x00, 0x00, 0x00) + IntToStrX(obj.amtsequence++) + ShortToStrX(MaxTxBuffer) + ShortToStrX(TxTimeout) + ShortToStrX(TxOverflowTimeout) + ShortToStrX(RxTimeout) + ShortToStrX(RxFlushTimeout) + ShortToStrX(Heartbeat) + IntToStrX(0));
|
||
}
|
||
if (obj.protocol == 2) {
|
||
// Remote Desktop: Send traffic directly...
|
||
obj.xxSend(String.fromCharCode(0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
|
||
}
|
||
if (obj.protocol == 3) {
|
||
// Remote IDER: Send traffic directly...
|
||
obj.connectstate = 1;
|
||
obj.xxStateChange(3);
|
||
}
|
||
} else obj.Stop(3);
|
||
break;
|
||
case 0x21: // Response to settings (33)
|
||
if (obj.amtaccumulator.length < 23) break;
|
||
cmdsize = 23;
|
||
obj.xxSend(String.fromCharCode(0x27, 0x00, 0x00, 0x00) + IntToStrX(obj.amtsequence++) + String.fromCharCode(0x00, 0x00, 0x1B, 0x00, 0x00, 0x00));
|
||
if (obj.protocol == 1) { obj.amtkeepalivetimer = setInterval(obj.xxSendAmtKeepAlive, 2000); }
|
||
obj.connectstate = 1;
|
||
obj.xxStateChange(3);
|
||
break;
|
||
case 0x29: // Serial Settings (41)
|
||
if (obj.amtaccumulator.length < 10) break;
|
||
cmdsize = 10;
|
||
break;
|
||
case 0x2A: // Incoming display data (42)
|
||
if (obj.amtaccumulator.length < 10) break;
|
||
var cs = (10 + ((obj.amtaccumulator.charCodeAt(9) & 0xFF) << 8) + (obj.amtaccumulator.charCodeAt(8) & 0xFF));
|
||
if (obj.amtaccumulator.length < cs) break;
|
||
obj.m.ProcessData(obj.amtaccumulator.substring(10, cs));
|
||
cmdsize = cs;
|
||
break;
|
||
case 0x2B: // Keep alive message (43)
|
||
if (obj.amtaccumulator.length < 8) break;
|
||
cmdsize = 8;
|
||
break;
|
||
case 0x41:
|
||
if (obj.amtaccumulator.length < 8) break;
|
||
obj.connectstate = 1;
|
||
obj.m.Start();
|
||
// KVM traffic, forward rest of accumulator directly.
|
||
if (obj.amtaccumulator.length > 8) { obj.m.ProcessData(obj.amtaccumulator.substring(8)); }
|
||
cmdsize = obj.amtaccumulator.length;
|
||
break;
|
||
default:
|
||
console.log("Unknown Intel AMT command: " + obj.amtaccumulator.charCodeAt(0) + " acclen=" + obj.amtaccumulator.length);
|
||
obj.Stop(4);
|
||
return;
|
||
}
|
||
if (cmdsize == 0) return;
|
||
obj.amtaccumulator = obj.amtaccumulator.substring(cmdsize);
|
||
}
|
||
}
|
||
|
||
obj.xxSend = function (x) {
|
||
//obj.Debug("Redir Send(" + x.length + "): " + rstr2hex(x));
|
||
if (obj.socket != null && obj.socket.readyState == WebSocket.OPEN) {
|
||
if (obj.debugmode == 1) { console.log('Send', x); }
|
||
var b = new Uint8Array(x.length);
|
||
for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); }
|
||
obj.socket.send(b.buffer);
|
||
}
|
||
}
|
||
|
||
obj.send = function (x) {
|
||
if (obj.socket == null || obj.connectstate != 1) return;
|
||
if (obj.protocol == 1) { obj.xxSend(String.fromCharCode(0x28, 0x00, 0x00, 0x00) + IntToStrX(obj.amtsequence++) + ShortToStrX(x.length) + x); } else { obj.xxSend(x); }
|
||
}
|
||
|
||
obj.xxSendAmtKeepAlive = function () {
|
||
if (obj.socket == null) return;
|
||
obj.xxSend(String.fromCharCode(0x2B, 0x00, 0x00, 0x00) + IntToStrX(obj.amtsequence++));
|
||
}
|
||
|
||
obj.xxRandomNonceX = "abcdef0123456789";
|
||
obj.xxRandomNonce = function (length) {
|
||
var r = "";
|
||
for (var i = 0; i < length; i++) { r += obj.xxRandomNonceX.charAt(Math.floor(Math.random() * obj.xxRandomNonceX.length)); }
|
||
return r;
|
||
}
|
||
|
||
obj.xxOnSocketClosed = function () {
|
||
if (obj.debugmode == 1) { console.log('onSocketClosed'); }
|
||
//obj.Debug("Redir Socket Closed");
|
||
if ((obj.inDataCount == 0) && (obj.tlsv1only == 0)) {
|
||
obj.tlsv1only = 1;
|
||
obj.socket = new WebSocket(window.location.protocol.replace("http", "ws") + "//" + window.location.host + window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/')) + "/webrelay.ashx?p=2&host=" + obj.host + "&port=" + obj.port + "&tls=" + obj.tls + "&tls1only=1" + ((obj.user == '*') ? "&serverauth=1" : "") + ((typeof pass === "undefined") ? ("&serverauth=1&user=" + obj.user) : "")); // The "p=2" indicates to the relay that this is a REDIRECTION session
|
||
obj.socket.onopen = obj.xxOnSocketConnected;
|
||
obj.socket.onmessage = obj.xxOnMessage;
|
||
obj.socket.onclose = obj.xxOnSocketClosed;
|
||
} else {
|
||
obj.Stop(5);
|
||
}
|
||
}
|
||
|
||
obj.xxStateChange = function(newstate) {
|
||
if (obj.State == newstate) return;
|
||
obj.State = newstate;
|
||
obj.m.xxStateChange(obj.State);
|
||
if (obj.onStateChanged != null) obj.onStateChanged(obj, obj.State);
|
||
}
|
||
|
||
obj.Stop = function (x) {
|
||
if (obj.debugmode == 1) { console.log('onSocketStop', x); }
|
||
//obj.Debug("Redir Socket Stopped");
|
||
obj.xxStateChange(0);
|
||
obj.connectstate = -1;
|
||
obj.amtaccumulator = "";
|
||
if (obj.socket != null) { obj.socket.close(); obj.socket = null; }
|
||
if (obj.amtkeepalivetimer != null) { clearInterval(obj.amtkeepalivetimer); obj.amtkeepalivetimer = null; }
|
||
}
|
||
|
||
obj.RedirectStartSol = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x53, 0x4F, 0x4C, 0x20);
|
||
obj.RedirectStartKvm = String.fromCharCode(0x10, 0x01, 0x00, 0x00, 0x4b, 0x56, 0x4d, 0x52);
|
||
obj.RedirectStartIder = String.fromCharCode(0x10, 0x00, 0x00, 0x00, 0x49, 0x44, 0x45, 0x52);
|
||
|
||
return obj;
|
||
}
|
||
/**
|
||
* @description WSMAN communication using websocket
|
||
* @author Ylian Saint-Hilaire
|
||
* @version v0.2.0c
|
||
*/
|
||
|
||
// Construct a WSMAN communication object
|
||
var CreateWsmanComm = function (host, port, user, pass, tls) {
|
||
var obj = {};
|
||
obj.PendingAjax = []; // List of pending AJAX calls. When one frees up, another will start.
|
||
obj.ActiveAjaxCount = 0; // Number of currently active AJAX calls
|
||
obj.MaxActiveAjaxCount = 1; // Maximum number of activate AJAX calls at the same time.
|
||
obj.FailAllError = 0; // Set this to non-zero to fail all AJAX calls with that error status, 999 causes responses to be silent.
|
||
obj.challengeParams = null;
|
||
obj.noncecounter = 1;
|
||
obj.authcounter = 0;
|
||
obj.socket = null;
|
||
obj.socketState = 0;
|
||
obj.host = host;
|
||
obj.port = port;
|
||
obj.user = user;
|
||
obj.pass = pass;
|
||
obj.tls = tls;
|
||
obj.tlsv1only = 1;
|
||
obj.cnonce = Math.random().toString(36).substring(7); // Generate a random client nonce
|
||
|
||
// Private method
|
||
//obj.Debug = function (msg) { console.log(msg); }
|
||
|
||
// Private method
|
||
// pri = priority, if set to 1, the call is high priority and put on top of the stack.
|
||
obj.PerformAjax = function (postdata, callback, tag, pri, url, action) {
|
||
if (obj.ActiveAjaxCount < obj.MaxActiveAjaxCount && obj.PendingAjax.length == 0) {
|
||
// There are no pending AJAX calls, perform the call now.
|
||
obj.PerformAjaxEx(postdata, callback, tag, url, action);
|
||
} else {
|
||
// If this is a high priority call, put this call in front of the array, otherwise put it in the back.
|
||
if (pri == 1) { obj.PendingAjax.unshift([postdata, callback, tag, url, action]); } else { obj.PendingAjax.push([postdata, callback, tag, url, action]); }
|
||
}
|
||
}
|
||
|
||
// Private method
|
||
obj.PerformNextAjax = function () {
|
||
if (obj.ActiveAjaxCount >= obj.MaxActiveAjaxCount || obj.PendingAjax.length == 0) return;
|
||
var x = obj.PendingAjax.shift();
|
||
obj.PerformAjaxEx(x[0], x[1], x[2], x[3], x[4]);
|
||
obj.PerformNextAjax();
|
||
}
|
||
|
||
// Private method
|
||
obj.PerformAjaxEx = function (postdata, callback, tag, url, action) {
|
||
if (obj.FailAllError != 0) { obj.gotNextMessagesError({ status: obj.FailAllError }, 'error', null, [postdata, callback, tag, url, action]); return; }
|
||
if (!postdata) postdata = "";
|
||
//console.log("SEND: " + postdata); // DEBUG
|
||
|
||
// We are in a websocket relay environment
|
||
obj.ActiveAjaxCount++;
|
||
return obj.PerformAjaxExNodeJS(postdata, callback, tag, url, action);
|
||
}
|
||
|
||
// Websocket relay specific private method
|
||
obj.pendingAjaxCall = [];
|
||
|
||
// Websocket relay specific private method
|
||
obj.PerformAjaxExNodeJS = function (postdata, callback, tag, url, action) { obj.PerformAjaxExNodeJS2(postdata, callback, tag, url, action, 3); }
|
||
|
||
// Websocket relay specific private method
|
||
obj.PerformAjaxExNodeJS2 = function (postdata, callback, tag, url, action, retry) {
|
||
if (retry <= 0 || obj.FailAllError != 0) {
|
||
// Too many retry, fail here.
|
||
obj.ActiveAjaxCount--;
|
||
if (obj.FailAllError != 999) obj.gotNextMessages(null, 'error', { status: ((obj.FailAllError == 0) ? 408 : obj.FailAllError) }, [postdata, callback, tag, url, action]); // 408 is timeout error
|
||
obj.PerformNextAjax();
|
||
return;
|
||
}
|
||
obj.pendingAjaxCall.push([postdata, callback, tag, url, action, retry]);
|
||
if (obj.socketState == 0) { obj.xxConnectHttpSocket(); }
|
||
else if (obj.socketState == 2) { obj.sendRequest(postdata, url, action); }
|
||
}
|
||
|
||
// Websocket relay specific private method (Content Length Encoding)
|
||
obj.sendRequest = function (postdata, url, action) {
|
||
url = url ? url : "/wsman";
|
||
action = action ? action : "POST";
|
||
var h = action + " " + url + " HTTP/1.1\r\n";
|
||
if (obj.challengeParams != null) {
|
||
var response = hex_md5(hex_md5(obj.user + ':' + obj.challengeParams["realm"] + ':' + obj.pass) + ':' + obj.challengeParams["nonce"] + ':' + obj.noncecounter + ':' + obj.cnonce + ':' + obj.challengeParams["qop"] + ':' + hex_md5(action + ':' + url));
|
||
h += 'Authorization: ' + obj.renderDigest({ "username": obj.user, "realm": obj.challengeParams["realm"], "nonce": obj.challengeParams["nonce"], "uri": url, "qop": obj.challengeParams["qop"], "response": response, "nc": obj.noncecounter++, "cnonce": obj.cnonce }) + '\r\n';
|
||
}
|
||
//h += 'Host: ' + obj.host + ':' + obj.port + '\r\nContent-Length: ' + postdata.length + '\r\n\r\n' + postdata; // Use Content-Length
|
||
h += 'Host: ' + obj.host + ':' + obj.port + '\r\nTransfer-Encoding: chunked\r\n\r\n' + postdata.length.toString(16).toUpperCase() + '\r\n' + postdata + '\r\n0\r\n\r\n'; // Use Chunked-Encoding
|
||
_Send(h);
|
||
//obj.Debug("SEND: " + h); // Display send packet
|
||
}
|
||
|
||
// Websocket relay specific private method
|
||
obj.parseDigest = function (header) {
|
||
var t = header.substring(7).split(',');
|
||
for (i in t) t[i] = t[i].trim();
|
||
return t.reduce(function (obj, s) { var parts = s.split('='); obj[parts[0]] = parts[1].replace(/"/g, ''); return obj; }, {})
|
||
}
|
||
|
||
// Websocket relay specific private method
|
||
obj.renderDigest = function (params) {
|
||
var paramsnames = [];
|
||
for (i in params) { paramsnames.push(i); }
|
||
return 'Digest ' + paramsnames.reduce(function (s1, ii) { return s1 + ',' + ii + '="' + params[ii] + '"' }, '').substring(1);
|
||
}
|
||
|
||
// Websocket relay specific private method
|
||
obj.xxConnectHttpSocket = function () {
|
||
//obj.Debug("xxConnectHttpSocket");
|
||
obj.socketParseState = 0;
|
||
obj.socketAccumulator = '';
|
||
obj.socketHeader = null;
|
||
obj.socketData = '';
|
||
obj.socketState = 1;
|
||
|
||
console.log(obj.tlsv1only);
|
||
obj.socket = new WebSocket(window.location.protocol.replace("http", "ws") + "//" + window.location.host + window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/')) + "/webrelay.ashx?p=1&host=" + obj.host + "&port=" + obj.port + "&tls=" + obj.tls + "&tlsv1only=" + obj.tlsv1only + ((user == '*') ? "&serverauth=1" : "") + ((typeof pass === "undefined") ? ("&serverauth=1&user=" + user) : "")); // The "p=1" indicates to the relay that this is a WSMAN session
|
||
obj.socket.onopen = _OnSocketConnected;
|
||
obj.socket.onmessage = _OnMessage;
|
||
obj.socket.onclose = _OnSocketClosed;
|
||
}
|
||
|
||
// Websocket relay specific private method
|
||
function _OnSocketConnected() {
|
||
//obj.Debug("xxOnSocketConnected");
|
||
obj.socketState = 2;
|
||
for (i in obj.pendingAjaxCall) { obj.sendRequest(obj.pendingAjaxCall[i][0], obj.pendingAjaxCall[i][3], obj.pendingAjaxCall[i][4]); }
|
||
}
|
||
|
||
// Setup the file reader
|
||
var fileReader = new FileReader();
|
||
var fileReaderInuse = false, fileReaderAcc = [];
|
||
if (fileReader.readAsBinaryString) {
|
||
// Chrome & Firefox (Draft)
|
||
fileReader.onload = function (e) { _OnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsBinaryString(new Blob([fileReaderAcc.shift()])); } }
|
||
} else if (fileReader.readAsArrayBuffer) {
|
||
// Chrome & Firefox (Spec)
|
||
fileReader.onloadend = function (e) { _OnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsArrayBuffer(fileReaderAcc.shift()); } }
|
||
}
|
||
|
||
function _OnMessage(e) {
|
||
if (typeof e.data == 'object') {
|
||
if (fileReaderInuse == true) { fileReaderAcc.push(e.data); return; }
|
||
if (fileReader.readAsBinaryString) {
|
||
// Chrome & Firefox (Draft)
|
||
fileReaderInuse = true;
|
||
fileReader.readAsBinaryString(new Blob([e.data]));
|
||
} else if (fileReader.readAsArrayBuffer) {
|
||
// Chrome & Firefox (Spec)
|
||
fileReaderInuse = true;
|
||
fileReader.readAsArrayBuffer(e.data);
|
||
} else {
|
||
// IE10, readAsBinaryString does not exist, use an alternative.
|
||
var binary = "", bytes = new Uint8Array(e.data), length = bytes.byteLength;
|
||
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
|
||
_OnSocketData(binary);
|
||
}
|
||
} else {
|
||
_OnSocketData(e.data);
|
||
}
|
||
};
|
||
|
||
// Websocket relay specific private method
|
||
function _OnSocketData(data) {
|
||
//obj.Debug("_OnSocketData (" + data.length + "): " + data);
|
||
|
||
if (typeof data === 'object') {
|
||
// This is an ArrayBuffer, convert it to a string array (used in IE)
|
||
var binary = "", bytes = new Uint8Array(data), length = bytes.byteLength;
|
||
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
|
||
data = binary;
|
||
}
|
||
else if (typeof data !== 'string') return;
|
||
|
||
//console.log("RECV: " + data); // DEBUG
|
||
|
||
obj.socketAccumulator += data;
|
||
while (true) {
|
||
if (obj.socketParseState == 0) {
|
||
var headersize = obj.socketAccumulator.indexOf("\r\n\r\n");
|
||
if (headersize < 0) return;
|
||
//obj.Debug(obj.socketAccumulator.substring(0, headersize)); // Display received HTTP header
|
||
obj.socketHeader = obj.socketAccumulator.substring(0, headersize).split("\r\n");
|
||
obj.socketAccumulator = obj.socketAccumulator.substring(headersize + 4);
|
||
obj.socketParseState = 1;
|
||
obj.socketData = '';
|
||
obj.socketXHeader = { Directive: obj.socketHeader[0].split(' ') };
|
||
for (i in obj.socketHeader) {
|
||
if (i != 0) {
|
||
var x2 = obj.socketHeader[i].indexOf(':');
|
||
obj.socketXHeader[obj.socketHeader[i].substring(0, x2).toLowerCase()] = obj.socketHeader[i].substring(x2 + 2);
|
||
}
|
||
}
|
||
}
|
||
if (obj.socketParseState == 1) {
|
||
var csize = -1;
|
||
if ((obj.socketXHeader["connection"] != undefined) && (obj.socketXHeader["connection"].toLowerCase() == 'close') && ((obj.socketXHeader["transfer-encoding"] == undefined) || (obj.socketXHeader["transfer-encoding"].toLowerCase() != 'chunked'))) {
|
||
// The body ends with a close, in this case, we will only process the header
|
||
csize = 0;
|
||
} else if (obj.socketXHeader["content-length"] != undefined) {
|
||
// The body length is specified by the content-length
|
||
csize = parseInt(obj.socketXHeader["content-length"]);
|
||
if (obj.socketAccumulator.length < csize) return;
|
||
var data = obj.socketAccumulator.substring(0, csize);
|
||
obj.socketAccumulator = obj.socketAccumulator.substring(csize);
|
||
obj.socketData = data;
|
||
csize = 0;
|
||
} else {
|
||
// The body is chunked
|
||
var clen = obj.socketAccumulator.indexOf("\r\n");
|
||
if (clen < 0) return; // Chunk length not found, exit now and get more data.
|
||
// Chunk length if found, lets see if we can get the data.
|
||
csize = parseInt(obj.socketAccumulator.substring(0, clen), 16);
|
||
if (isNaN(csize)) { if (obj.websocket) { obj.websocket.close(); } return; } // Critical error, close the socket and exit.
|
||
if (obj.socketAccumulator.length < clen + 2 + csize + 2) return;
|
||
// We got a chunk with all of the data, handle the chunck now.
|
||
var data = obj.socketAccumulator.substring(clen + 2, clen + 2 + csize);
|
||
obj.socketAccumulator = obj.socketAccumulator.substring(clen + 2 + csize + 2);
|
||
obj.socketData += data;
|
||
}
|
||
if (csize == 0) {
|
||
//obj.Debug("_OnSocketData DONE: (" + obj.socketData.length + "): " + obj.socketData);
|
||
_ProcessHttpResponse(obj.socketXHeader, obj.socketData);
|
||
obj.socketParseState = 0;
|
||
obj.socketHeader = null;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Websocket relay specific private method
|
||
function _ProcessHttpResponse(header, data) {
|
||
//obj.Debug("_ProcessHttpResponse: " + header.Directive[1]);
|
||
|
||
var s = parseInt(header.Directive[1]);
|
||
if (isNaN(s)) s = 602;
|
||
if (s == 401 && ++(obj.authcounter) < 3) {
|
||
obj.challengeParams = obj.parseDigest(header['www-authenticate']); // Set the digest parameters, after this, the socket will close and we will auto-retry
|
||
} else {
|
||
var r = obj.pendingAjaxCall.shift();
|
||
// if (s != 200) { obj.Debug("Error, status=" + s + "\r\n\r\nreq=" + r[0] + "\r\n\r\nresp=" + data); } // Debug: Display the request & response if something did not work.
|
||
obj.authcounter = 0;
|
||
obj.ActiveAjaxCount--;
|
||
obj.gotNextMessages(data, 'success', { status: s }, r);
|
||
obj.PerformNextAjax();
|
||
}
|
||
}
|
||
|
||
// Websocket relay specific private method
|
||
function _OnSocketClosed(data) {
|
||
//obj.Debug("_OnSocketClosed");
|
||
obj.socketState = 0;
|
||
if (obj.socket != null) { obj.socket.close(); obj.socket = null; }
|
||
if (obj.pendingAjaxCall.length > 0) {
|
||
var r = obj.pendingAjaxCall.shift();
|
||
var retry = r[5];
|
||
obj.PerformAjaxExNodeJS2(r[0], r[1], r[2], r[3], r[4], --retry);
|
||
}
|
||
}
|
||
|
||
// Websocket relay specific private method
|
||
function _Send(x) {
|
||
//console.log("SEND: " + x); // DEBUG
|
||
if (obj.socketState == 2 && obj.socket != null && obj.socket.readyState == WebSocket.OPEN) {
|
||
var b = new Uint8Array(x.length);
|
||
for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); }
|
||
try { obj.socket.send(b.buffer); } catch (e) { }
|
||
}
|
||
}
|
||
|
||
// Private method
|
||
obj.gotNextMessages = function (data, status, request, callArgs) {
|
||
if (obj.FailAllError == 999) return;
|
||
if (obj.FailAllError != 0) { callArgs[1](null, obj.FailAllError, callArgs[2]); return; }
|
||
if (request.status != 200) { callArgs[1](null, request.status, callArgs[2]); return; }
|
||
callArgs[1](data, 200, callArgs[2]);
|
||
}
|
||
|
||
// Private method
|
||
obj.gotNextMessagesError = function (request, status, errorThrown, callArgs) {
|
||
if (obj.FailAllError == 999) return;
|
||
if (obj.FailAllError != 0) { callArgs[1](null, obj.FailAllError, callArgs[2]); return; }
|
||
callArgs[1](obj, null, { Header: { HttpError: request.status } }, request.status, callArgs[2]);
|
||
}
|
||
|
||
// Cancel all pending queries with given status
|
||
obj.CancelAllQueries = function (s) {
|
||
while (obj.PendingAjax.length > 0) { var x = obj.PendingAjax.shift(); x[1](null, s, x[2]); }
|
||
if (obj.websocket != null) { obj.websocket.close(); obj.websocket = null; obj.socketState = 0; }
|
||
}
|
||
|
||
return obj;
|
||
}
|
||
|
||
/**
|
||
* @description Mesh Agent Transport Module - using websocket relay
|
||
* @author Ylian Saint-Hilaire
|
||
* @version v0.0.1f
|
||
*/
|
||
|
||
// Construct a MeshServer agent direction object
|
||
var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort, authCookie, domainUrl) {
|
||
var obj = {};
|
||
obj.m = module; // This is the inner module (Terminal or Desktop)
|
||
module.parent = obj;
|
||
obj.meshserver = meshserver;
|
||
obj.authCookie = authCookie;
|
||
obj.State = 0;
|
||
obj.nodeid = null;
|
||
obj.socket = null;
|
||
obj.connectstate = -1;
|
||
obj.tunnelid = Math.random().toString(36).substring(2); // Generate a random client tunnel id
|
||
obj.protocol = module.protocol; // 1 = SOL, 2 = KVM, 3 = IDER, 4 = Files, 5 = FileTransfer
|
||
obj.onStateChanged = null;
|
||
obj.ctrlMsgAllowed = true;
|
||
obj.attemptWebRTC = false;
|
||
obj.webRtcActive = false;
|
||
obj.webSwitchOk = false;
|
||
obj.webchannel = null;
|
||
obj.webrtc = null;
|
||
obj.debugmode = 0;
|
||
if (domainUrl == null) { domainUrl = '/'; }
|
||
|
||
// Console Message
|
||
obj.consoleMessage = null;
|
||
obj.onConsoleMessageChange = null;
|
||
|
||
// Private method
|
||
//obj.debug = function (msg) { console.log(msg); }
|
||
|
||
obj.Start = function (nodeid) {
|
||
var url2, url = window.location.protocol.replace("http", "ws") + "//" + window.location.host + window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/')) + "/meshrelay.ashx?id=" + obj.tunnelid;
|
||
//if (serverPublicNamePort) { url2 = window.location.protocol.replace("http", "ws") + "//" + serverPublicNamePort + "/meshrelay.ashx?id=" + obj.tunnelid; } else { url2 = url; }
|
||
if ((authCookie != null) && (authCookie != '')) { url += '&auth=' + authCookie; }
|
||
obj.nodeid = nodeid;
|
||
obj.connectstate = 0;
|
||
obj.socket = new WebSocket(url);
|
||
obj.socket.onopen = obj.xxOnSocketConnected;
|
||
obj.socket.onmessage = obj.xxOnMessage;
|
||
//obj.socket.onmessage = function (e) { console.log('Websocket data', e.data); obj.xxOnMessage(e); }
|
||
obj.socket.onerror = function (e) { /* console.error(e); */ }
|
||
obj.socket.onclose = obj.xxOnSocketClosed;
|
||
obj.xxStateChange(1);
|
||
//obj.meshserver.send({ action: 'msg', type: 'tunnel', nodeid: obj.nodeid, value: url2 });
|
||
obj.meshserver.send({ action: 'msg', type: 'tunnel', nodeid: obj.nodeid, value: "*" + domainUrl + "meshrelay.ashx?id=" + obj.tunnelid, usage: obj.protocol });
|
||
//obj.debug("Agent Redir Start: " + url);
|
||
}
|
||
|
||
obj.xxOnSocketConnected = function () {
|
||
if (obj.debugmode == 1) { console.log('onSocketConnected'); }
|
||
//obj.debug("Agent Redir Socket Connected");
|
||
obj.xxStateChange(2);
|
||
}
|
||
|
||
// Called to pass websocket control messages
|
||
obj.xxOnControlCommand = function (msg) {
|
||
var controlMsg;
|
||
try { controlMsg = JSON.parse(msg); } catch (e) { return; }
|
||
if (controlMsg.ctrlChannel != '102938') { obj.xxOnSocketData(msg); return; }
|
||
//console.log(controlMsg);
|
||
if (controlMsg.type == 'console') {
|
||
obj.consoleMessage = controlMsg.msg;
|
||
if (obj.onConsoleMessageChange) { obj.onConsoleMessageChange(obj, obj.consoleMessage); }
|
||
} else if (obj.webrtc != null) {
|
||
if (controlMsg.type == 'answer') {
|
||
obj.webrtc.setRemoteDescription(new RTCSessionDescription(controlMsg), function () { /*console.log('WebRTC remote ok');*/ }, obj.xxCloseWebRTC);
|
||
} else if (controlMsg.type == 'webrtc0') {
|
||
obj.webSwitchOk = true; // Other side is ready for switch over
|
||
performWebRtcSwitch();
|
||
} else if (controlMsg.type == 'webrtc1') {
|
||
obj.sendCtrlMsg("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc2\"}"); // Confirm we got end of data marker, indicates data will no longer be received on websocket.
|
||
} else if (controlMsg.type == 'webrtc2') {
|
||
// TODO: Resume/Start sending data over WebRTC
|
||
}
|
||
}
|
||
}
|
||
|
||
obj.sendCtrlMsg = function (x) { if (obj.ctrlMsgAllowed == true) { if ((typeof args != 'undefined') && args.redirtrace) { console.log('RedirSend', typeof x, x); } try { obj.socket.send(x); } catch (ex) { } } }
|
||
|
||
function performWebRtcSwitch() {
|
||
if ((obj.webSwitchOk == true) && (obj.webRtcActive == true)) {
|
||
obj.sendCtrlMsg("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc0\"}"); // Indicate to the meshagent that it can start traffic switchover
|
||
obj.sendCtrlMsg("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc1\"}"); // Indicate to the meshagent that data traffic will no longer be sent over websocket.
|
||
// TODO: Hold/Stop sending data over websocket
|
||
if (obj.onStateChanged != null) { obj.onStateChanged(obj, obj.State); }
|
||
}
|
||
}
|
||
|
||
obj.xxOnMessage = function (e) {
|
||
//console.log('Recv', e.data, e.data.byteLength, obj.State);
|
||
if (obj.State < 3) {
|
||
if (e.data == 'c') {
|
||
try { obj.socket.send(obj.protocol); } catch (ex) { }
|
||
obj.xxStateChange(3);
|
||
|
||
if (obj.attemptWebRTC == true) {
|
||
// Try to get WebRTC setup
|
||
var configuration = null; //{ "iceServers": [ { 'urls': 'stun:stun.services.mozilla.com' }, { 'urls': 'stun:stun.l.google.com:19302' } ] };
|
||
if (typeof RTCPeerConnection !== 'undefined') { obj.webrtc = new RTCPeerConnection(configuration); }
|
||
else if (typeof webkitRTCPeerConnection !== 'undefined') { obj.webrtc = new webkitRTCPeerConnection(configuration); }
|
||
if (obj.webrtc != null) {
|
||
obj.webchannel = obj.webrtc.createDataChannel("DataChannel", {}); // { ordered: false, maxRetransmits: 2 }
|
||
obj.webchannel.onmessage = obj.xxOnMessage;
|
||
//obj.webchannel.onmessage = function (e) { console.log('WebRTC data', e.data); obj.xxOnMessage(e); }
|
||
obj.webchannel.onopen = function () { obj.webRtcActive = true; performWebRtcSwitch(); };
|
||
obj.webchannel.onclose = function (event) { if (obj.webRtcActive) { obj.Stop(); } }
|
||
obj.webrtc.onicecandidate = function (e) {
|
||
if (e.candidate == null) {
|
||
try { obj.socket.send(JSON.stringify(obj.webrtcoffer)); } catch (ex) { } // End of candidates, send the offer
|
||
} else {
|
||
obj.webrtcoffer.sdp += ("a=" + e.candidate.candidate + "\r\n"); // New candidate, add it to the SDP
|
||
}
|
||
}
|
||
obj.webrtc.oniceconnectionstatechange = function () {
|
||
if (obj.webrtc != null) {
|
||
if (obj.webrtc.iceConnectionState == 'disconnected') { if (obj.webRtcActive == true) { obj.Stop(); } else { obj.xxCloseWebRTC(); } }
|
||
else if (obj.webrtc.iceConnectionState == 'failed') { obj.xxCloseWebRTC(); }
|
||
}
|
||
}
|
||
obj.webrtc.createOffer(function (offer) {
|
||
// Got the offer
|
||
obj.webrtcoffer = offer;
|
||
obj.webrtc.setLocalDescription(offer, function () { /*console.log('WebRTC local ok');*/ }, obj.xxCloseWebRTC);
|
||
}, obj.xxCloseWebRTC, { mandatory: { OfferToReceiveAudio: false, OfferToReceiveVideo: false } });
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (typeof e.data == 'string') {
|
||
// Control messages, most likely WebRTC setup
|
||
obj.xxOnControlCommand(e.data);
|
||
return;
|
||
}
|
||
|
||
/*
|
||
if (typeof e.data == 'object') {
|
||
var f = new FileReader();
|
||
if (f.readAsBinaryString) {
|
||
// Chrome & Firefox (Draft)
|
||
f.onload = function (e) { obj.xxOnSocketData(e.target.result); }
|
||
f.readAsBinaryString(new Blob([e.data]));
|
||
} else if (f.readAsArrayBuffer) {
|
||
// Chrome & Firefox (Spec)
|
||
f.onloadend = function (e) { obj.xxOnSocketData(e.target.result); }
|
||
f.readAsArrayBuffer(e.data);
|
||
} else {
|
||
// IE10, readAsBinaryString does not exist, use an alternative.
|
||
var binary = "";
|
||
var bytes = new Uint8Array(e.data);
|
||
var length = bytes.byteLength;
|
||
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
|
||
obj.xxOnSocketData(binary);
|
||
}
|
||
} else {
|
||
// If we get a string object, it maybe the WebRTC confirm. Ignore it.
|
||
obj.xxOnSocketData(e.data);
|
||
}
|
||
*/
|
||
|
||
if (typeof e.data == 'object') {
|
||
if (fileReaderInuse == true) { fileReaderAcc.push(e.data); return; }
|
||
if (fileReader.readAsBinaryString) {
|
||
// Chrome & Firefox (Draft)
|
||
fileReaderInuse = true;
|
||
fileReader.readAsBinaryString(new Blob([e.data]));
|
||
} else if (fileReader.readAsArrayBuffer) {
|
||
// Chrome & Firefox (Spec)
|
||
fileReaderInuse = true;
|
||
fileReader.readAsArrayBuffer(e.data);
|
||
} else {
|
||
// IE10, readAsBinaryString does not exist, use an alternative.
|
||
var binary = "", bytes = new Uint8Array(e.data), length = bytes.byteLength;
|
||
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
|
||
obj.xxOnSocketData(binary);
|
||
}
|
||
} else {
|
||
// If we get a string object, it maybe the WebRTC confirm. Ignore it.
|
||
obj.xxOnSocketData(e.data);
|
||
}
|
||
};
|
||
|
||
// Setup the file reader
|
||
var fileReader = new FileReader();
|
||
var fileReaderInuse = false, fileReaderAcc = [];
|
||
if (fileReader.readAsBinaryString) {
|
||
// Chrome & Firefox (Draft)
|
||
fileReader.onload = function (e) { obj.xxOnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsBinaryString(new Blob([fileReaderAcc.shift()])); } }
|
||
} else if (fileReader.readAsArrayBuffer) {
|
||
// Chrome & Firefox (Spec)
|
||
fileReader.onloadend = function (e) { obj.xxOnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsArrayBuffer(fileReaderAcc.shift()); } }
|
||
}
|
||
|
||
obj.xxOnSocketData = function (data) {
|
||
if (!data || obj.connectstate == -1) return;
|
||
if (typeof data === 'object') {
|
||
// This is an ArrayBuffer, convert it to a string array (used in IE)
|
||
var binary = "", bytes = new Uint8Array(data), length = bytes.byteLength;
|
||
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
|
||
data = binary;
|
||
}
|
||
else if (typeof data !== 'string') return;
|
||
//console.log("xxOnSocketData", rstr2hex(data));
|
||
if ((typeof args != 'undefined') && args.redirtrace) { console.log("RedirRecv", typeof data, data.length, data); }
|
||
return obj.m.ProcessData(data);
|
||
}
|
||
|
||
obj.sendText = function (x) {
|
||
if (typeof x != 'string') { x = JSON.stringify(x); } // Turn into a string if needed
|
||
obj.send(encode_utf8(x)); // Encode UTF8 correctly
|
||
}
|
||
|
||
obj.send = function (x) {
|
||
//obj.debug("Agent Redir Send(" + obj.webRtcActive + ", " + x.length + "): " + rstr2hex(x));
|
||
//console.log("Agent Redir Send(" + obj.webRtcActive + ", " + x.length + "): " + ((typeof x == 'string')?x:rstr2hex(x)));
|
||
if ((typeof args != 'undefined') && args.redirtrace) { console.log('RedirSend', typeof x, x.length, x); }
|
||
try {
|
||
if (obj.socket != null && obj.socket.readyState == WebSocket.OPEN) {
|
||
if (typeof x == 'string') {
|
||
if (obj.debugmode == 1) {
|
||
var b = new Uint8Array(x.length), c = [];
|
||
for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); c.push(x.charCodeAt(i)); }
|
||
if (obj.webRtcActive == true) { obj.webchannel.send(b.buffer); } else { obj.socket.send(b.buffer); }
|
||
//console.log('Send', c);
|
||
} else {
|
||
var b = new Uint8Array(x.length);
|
||
for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); }
|
||
if (obj.webRtcActive == true) { obj.webchannel.send(b.buffer); } else { obj.socket.send(b.buffer); }
|
||
}
|
||
} else {
|
||
//if (obj.debugmode == 1) { console.log('Send', x); }
|
||
if (obj.webRtcActive == true) { obj.webchannel.send(x); } else { obj.socket.send(x); }
|
||
}
|
||
}
|
||
} catch (ex) { }
|
||
}
|
||
|
||
obj.xxOnSocketClosed = function () {
|
||
//obj.debug("Agent Redir Socket Closed");
|
||
//if (obj.debugmode == 1) { console.log('onSocketClosed'); }
|
||
obj.Stop(1);
|
||
}
|
||
|
||
obj.xxStateChange = function(newstate) {
|
||
if (obj.State == newstate) return;
|
||
obj.State = newstate;
|
||
obj.m.xxStateChange(obj.State);
|
||
if (obj.onStateChanged != null) obj.onStateChanged(obj, obj.State);
|
||
}
|
||
|
||
// Close the WebRTC connection, should be called if a problem occurs during WebRTC setup.
|
||
obj.xxCloseWebRTC = function () {
|
||
if (obj.webchannel != null) { try { obj.webchannel.close(); } catch (e) { } obj.webchannel = null; }
|
||
if (obj.webrtc != null) { try { obj.webrtc.close(); } catch (e) { } obj.webrtc = null; }
|
||
obj.webRtcActive = false;
|
||
}
|
||
|
||
obj.Stop = function (x) {
|
||
if (obj.debugmode == 1) { console.log('stop', x); }
|
||
|
||
// Clean up WebRTC
|
||
obj.xxCloseWebRTC();
|
||
|
||
//obj.debug("Agent Redir Socket Stopped");
|
||
obj.connectstate = -1;
|
||
if (obj.socket != null) {
|
||
try { if (obj.socket.readyState == 1) { obj.sendCtrlMsg("{\"ctrlChannel\":\"102938\",\"type\":\"close\"}"); obj.socket.close(); } } catch (e) { }
|
||
obj.socket = null;
|
||
}
|
||
obj.xxStateChange(0);
|
||
}
|
||
|
||
return obj;
|
||
}
|
||
/**
|
||
* @description Mesh Agent Transport Module - using websocket relay
|
||
* @author Ylian Saint-Hilaire
|
||
* @version v0.0.1
|
||
*/
|
||
|
||
// Construct a MeshServer agent direction object
|
||
var CreateKvmDataChannel = function (webchannel, module, keepalive) {
|
||
var obj = {};
|
||
obj.m = module; // This is the inner module (Terminal or Desktop)
|
||
module.parent = obj;
|
||
obj.webchannel = webchannel;
|
||
obj.State = 0;
|
||
obj.protocol = module.protocol; // 1 = SOL, 2 = KVM, 3 = IDER, 4 = Files, 5 = FileTransfer
|
||
obj.onStateChanged = null;
|
||
obj.onControlMsg = null;
|
||
obj.debugmode = 0;
|
||
obj.keepalive = keepalive;
|
||
obj.rtcKeepAlive = null;
|
||
|
||
// Private method
|
||
//obj.debug = function (msg) { console.log(msg); }
|
||
|
||
obj.Start = function () {
|
||
if (obj.debugmode == 1) { console.log('start'); }
|
||
obj.xxStateChange(3);
|
||
obj.webchannel.onmessage = obj.xxOnMessage;
|
||
obj.rtcKeepAlive = setInterval(obj.xxSendRtcKeepAlive, 30000);
|
||
}
|
||
|
||
// Setup the file reader
|
||
var fileReader = new FileReader();
|
||
var fileReaderInuse = false, fileReaderAcc = [];
|
||
if (fileReader.readAsBinaryString) {
|
||
// Chrome & Firefox (Draft)
|
||
fileReader.onload = function (e) { obj.xxOnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsBinaryString(new Blob([fileReaderAcc.shift()])); } }
|
||
} else if (fileReader.readAsArrayBuffer) {
|
||
// Chrome & Firefox (Spec)
|
||
fileReader.onloadend = function (e) { obj.xxOnSocketData(e.target.result); if (fileReaderAcc.length == 0) { fileReaderInuse = false; } else { fileReader.readAsArrayBuffer(fileReaderAcc.shift()); } }
|
||
}
|
||
|
||
obj.xxOnMessage = function (e) {
|
||
//if (obj.debugmode == 1) { console.log('Recv', e.data); }
|
||
//if (urlvars && urlvars['webrtctrace']) { console.log('WebRTC-Recv(' + obj.State + '): ', typeof e.data, e.data); }
|
||
if (typeof e.data == 'string') { if (obj.onControlMsg != null) { obj.onControlMsg(e.data); } return; } // If this is a control message, handle it here.
|
||
if (typeof e.data == 'object') {
|
||
if (fileReaderInuse == true) { fileReaderAcc.push(e.data); return; }
|
||
if (fileReader.readAsBinaryString) {
|
||
// Chrome & Firefox (Draft)
|
||
fileReaderInuse = true;
|
||
fileReader.readAsBinaryString(new Blob([e.data]));
|
||
} else if (f.readAsArrayBuffer) {
|
||
// Chrome & Firefox (Spec)
|
||
fileReaderInuse = true;
|
||
fileReader.readAsArrayBuffer(e.data);
|
||
} else {
|
||
// IE10, readAsBinaryString does not exist, use an alternative.
|
||
var binary = "", bytes = new Uint8Array(e.data), length = bytes.byteLength;
|
||
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
|
||
obj.xxOnSocketData(binary);
|
||
}
|
||
} else {
|
||
// If we get a string object, it maybe the WebRTC confirm. Ignore it.
|
||
//obj.debug("Agent Redir Relay - OnData - " + typeof e.data + " - " + e.data.length);
|
||
obj.xxOnSocketData(e.data);
|
||
}
|
||
};
|
||
|
||
/*
|
||
obj.xxOnMessage = function (e) {
|
||
//if (obj.debugmode == 1) { console.log('Recv', e.data); }
|
||
//if (urlvars && urlvars['webrtctrace']) { console.log('WebRTC-Recv(' + obj.State + '): ', typeof e.data, e.data); }
|
||
if (typeof e.data == 'string') { if (obj.onControlMsg != null) { obj.onControlMsg(e.data); } return; } // If this is a control message, handle it here.
|
||
if (typeof e.data == 'object') {
|
||
var f = new FileReader();
|
||
if (f.readAsBinaryString) {
|
||
// Chrome & Firefox (Draft)
|
||
f.onload = function (e) { obj.xxOnSocketData(e.target.result); }
|
||
f.readAsBinaryString(new Blob([e.data]));
|
||
} else if (f.readAsArrayBuffer) {
|
||
// Chrome & Firefox (Spec)
|
||
f.onloadend = function (e) { obj.xxOnSocketData(e.target.result); }
|
||
f.readAsArrayBuffer(e.data);
|
||
} else {
|
||
// IE10, readAsBinaryString does not exist, use an alternative.
|
||
var binary = '', bytes = new Uint8Array(e.data), length = bytes.byteLength;
|
||
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
|
||
obj.xxOnSocketData(binary);
|
||
}
|
||
} else {
|
||
// If we get a string object, it maybe the WebRTC confirm. Ignore it.
|
||
//obj.debug("Agent Redir Relay - OnData - " + typeof e.data + " - " + e.data.length);
|
||
obj.xxOnSocketData(e.data);
|
||
}
|
||
};
|
||
*/
|
||
|
||
obj.xxOnSocketData = function (data) {
|
||
if (!data) return;
|
||
if (typeof data === 'object') {
|
||
// This is an ArrayBuffer, convert it to a string array (used in IE)
|
||
var binary = "", bytes = new Uint8Array(data), length = bytes.byteLength;
|
||
for (var i = 0; i < length; i++) { binary += String.fromCharCode(bytes[i]); }
|
||
data = binary;
|
||
}
|
||
else if (typeof data !== 'string') return;
|
||
//console.log("xxOnSocketData", rstr2hex(data));
|
||
return obj.m.ProcessData(data);
|
||
}
|
||
|
||
// Send a control message over the WebRTC data channel
|
||
obj.sendCtrlMsg = function (x) {
|
||
if (typeof x == 'string') {
|
||
obj.webchannel.send(x);
|
||
//if (urlvars && urlvars['webrtctrace']) { console.log('WebRTC-Send(' + obj.State + '): ', typeof x, x); }
|
||
if (obj.keepalive != null) obj.keepalive.sendKeepAlive();
|
||
}
|
||
}
|
||
|
||
// Send a binary message over the WebRTC data channel
|
||
obj.send = function (x) {
|
||
if (typeof x == 'string') { var b = new Uint8Array(x.length); for (var i = 0; i < x.length; ++i) { b[i] = x.charCodeAt(i); } x = b; }
|
||
//if (urlvars && urlvars['webrtctrace']) { console.log('WebRTC-Send(' + obj.State + '): ', typeof x, x); }
|
||
obj.webchannel.send(x);
|
||
}
|
||
|
||
obj.xxStateChange = function(newstate) {
|
||
if (obj.State == newstate) return;
|
||
obj.State = newstate;
|
||
obj.m.xxStateChange(obj.State);
|
||
if (obj.onStateChanged != null) obj.onStateChanged(obj, obj.State);
|
||
}
|
||
|
||
obj.Stop = function () {
|
||
if (obj.debugmode == 1) { console.log('stop'); }
|
||
if (obj.rtcKeepAlive != null) { clearInterval(obj.rtcKeepAlive); obj.rtcKeepAlive = null; }
|
||
obj.xxStateChange(0);
|
||
}
|
||
|
||
obj.xxSendRtcKeepAlive = function () {
|
||
//if (urlvars && urlvars['webrtctrace']) { console.log('WebRTC-SendKeepAlive()'); }
|
||
obj.sendCtrlMsg(JSON.stringify({ action: 'ping' }));
|
||
}
|
||
|
||
return obj;
|
||
}
|
||
/**
|
||
* @description Remote Desktop
|
||
* @author Ylian Saint-Hilaire
|
||
* @version v0.0.2g
|
||
*/
|
||
|
||
// Construct a MeshServer object
|
||
var CreateAgentRemoteDesktop = function (canvasid, scrolldiv) {
|
||
var obj = {}
|
||
obj.CanvasId = canvasid;
|
||
if (typeof canvasid === 'string') obj.CanvasId = Q(canvasid);
|
||
obj.Canvas = obj.CanvasId.getContext("2d");
|
||
obj.scrolldiv = scrolldiv;
|
||
obj.State = 0;
|
||
obj.PendingOperations = [];
|
||
obj.tilesReceived = 0;
|
||
obj.TilesDrawn = 0;
|
||
obj.KillDraw = 0;
|
||
obj.ipad = false;
|
||
obj.tabletKeyboardVisible = false;
|
||
obj.LastX = 0;
|
||
obj.LastY = 0;
|
||
obj.touchenabled = 0;
|
||
obj.submenuoffset = 0;
|
||
obj.touchtimer = null;
|
||
obj.TouchArray = {};
|
||
obj.connectmode = 0; // 0 = HTTP, 1 = WebSocket, 2 = WebRTC
|
||
obj.connectioncount = 0;
|
||
obj.rotation = 0;
|
||
obj.protocol = 2; // KVM
|
||
obj.debugmode = 0;
|
||
obj.firstUpKeys = [];
|
||
obj.stopInput = false;
|
||
obj.localKeyMap = true;
|
||
|
||
obj.sessionid = 0;
|
||
obj.username;
|
||
obj.oldie = false;
|
||
obj.CompressionLevel = 50;
|
||
obj.ScalingLevel = 1024;
|
||
obj.FrameRateTimer = 50;
|
||
obj.FirstDraw = false;
|
||
|
||
obj.ScreenWidth = 960;
|
||
obj.ScreenHeight = 700;
|
||
obj.width = 960;
|
||
obj.height = 960;
|
||
|
||
obj.onScreenSizeChange = null;
|
||
obj.onMessage = null;
|
||
obj.onConnectCountChanged = null;
|
||
obj.onDebugMessage = null;
|
||
obj.onTouchEnabledChanged = null;
|
||
obj.onDisplayinfo = null;
|
||
obj.accumulator = null;
|
||
|
||
obj.Start = function () {
|
||
obj.State = 0;
|
||
obj.accumulator = null;
|
||
}
|
||
|
||
obj.Stop = function () {
|
||
obj.setRotation(0);
|
||
obj.UnGrabKeyInput();
|
||
obj.UnGrabMouseInput();
|
||
obj.touchenabled = 0;
|
||
if (obj.onScreenSizeChange != null) obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId);
|
||
obj.Canvas.clearRect(0, 0, obj.CanvasId.width, obj.CanvasId.height);
|
||
}
|
||
|
||
obj.xxStateChange = function (newstate) {
|
||
if (obj.State == newstate) return;
|
||
obj.State = newstate;
|
||
obj.CanvasId.style.cursor = 'default';
|
||
//console.log('xxStateChange', newstate);
|
||
switch (newstate) {
|
||
case 0: {
|
||
// Disconnect
|
||
obj.Stop();
|
||
break;
|
||
}
|
||
case 3: {
|
||
// Websocket connected
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
obj.send = function (x) {
|
||
if (obj.debugmode > 1) { console.log("KSend(" + x.length + "): " + rstr2hex(x)); }
|
||
obj.parent.send(x);
|
||
}
|
||
|
||
// KVM Control.
|
||
// Routines for processing incoming packets from the AJAX server, and handling individual messages.
|
||
obj.ProcessPictureMsg = function (str, X, Y) {
|
||
//if (obj.targetnode != null) obj.Debug("ProcessPictureMsg " + X + "," + Y + " - " + obj.targetnode.substring(0, 8));
|
||
var tile = new Image();
|
||
tile.xcount = obj.tilesReceived++;
|
||
//console.log('Tile #' + tile.xcount);
|
||
var r = obj.tilesReceived;
|
||
tile.src = "data:image/jpeg;base64," + btoa(str.substring(4, str.length));
|
||
tile.onload = function () {
|
||
//console.log('DecodeTile #' + this.xcount);
|
||
if (obj.Canvas != null && obj.KillDraw < r && obj.State != 0) {
|
||
obj.PendingOperations.push([r, 2, tile, X, Y]);
|
||
while (obj.DoPendingOperations()) { }
|
||
}
|
||
}
|
||
tile.error = function () { console.log('DecodeTileError'); }
|
||
}
|
||
|
||
obj.DoPendingOperations = function () {
|
||
if (obj.PendingOperations.length == 0) return false;
|
||
for (var i = 0; i < obj.PendingOperations.length; i++) { // && KillDraw < tilesDrawn
|
||
var Msg = obj.PendingOperations[i];
|
||
if (Msg[0] == (obj.TilesDrawn + 1)) {
|
||
if (Msg[1] == 1) { obj.ProcessCopyRectMsg(Msg[2]); }
|
||
else if (Msg[1] == 2) { obj.Canvas.drawImage(Msg[2], obj.rotX(Msg[3], Msg[4]), obj.rotY(Msg[3], Msg[4])); delete Msg[2]; }
|
||
obj.PendingOperations.splice(i, 1);
|
||
delete Msg;
|
||
obj.TilesDrawn++;
|
||
if (obj.TilesDrawn == obj.tilesReceived && obj.KillDraw < obj.TilesDrawn) { obj.KillDraw = obj.TilesDrawn = obj.tilesReceived = 0; }
|
||
return true;
|
||
}
|
||
}
|
||
if (obj.oldie && obj.PendingOperations.length > 0) { obj.TilesDrawn++; }
|
||
return false;
|
||
}
|
||
|
||
obj.ProcessCopyRectMsg = function (str) {
|
||
var SX = ((str.charCodeAt(0) & 0xFF) << 8) + (str.charCodeAt(1) & 0xFF);
|
||
var SY = ((str.charCodeAt(2) & 0xFF) << 8) + (str.charCodeAt(3) & 0xFF);
|
||
var DX = ((str.charCodeAt(4) & 0xFF) << 8) + (str.charCodeAt(5) & 0xFF);
|
||
var DY = ((str.charCodeAt(6) & 0xFF) << 8) + (str.charCodeAt(7) & 0xFF);
|
||
var WIDTH = ((str.charCodeAt(8) & 0xFF) << 8) + (str.charCodeAt(9) & 0xFF);
|
||
var HEIGHT = ((str.charCodeAt(10) & 0xFF) << 8) + (str.charCodeAt(11) & 0xFF);
|
||
obj.Canvas.drawImage(Canvas.canvas, SX, SY, WIDTH, HEIGHT, DX, DY, WIDTH, HEIGHT);
|
||
}
|
||
|
||
obj.SendUnPause = function () {
|
||
//obj.Debug("SendUnPause");
|
||
//obj.xxStateChange(3);
|
||
obj.send(String.fromCharCode(0x00, 0x08, 0x00, 0x05, 0x00));
|
||
}
|
||
|
||
obj.SendPause = function () {
|
||
//obj.Debug("SendPause");
|
||
//obj.xxStateChange(2);
|
||
obj.send(String.fromCharCode(0x00, 0x08, 0x00, 0x05, 0x01));
|
||
}
|
||
|
||
obj.SendCompressionLevel = function (type, level, scaling, frametimer) {
|
||
if (level) { obj.CompressionLevel = level; }
|
||
if (scaling) { obj.ScalingLevel = scaling; }
|
||
if (frametimer) { obj.FrameRateTimer = frametimer; }
|
||
obj.send(String.fromCharCode(0x00, 0x05, 0x00, 0x0A, type, obj.CompressionLevel) + obj.shortToStr(obj.ScalingLevel) + obj.shortToStr(obj.FrameRateTimer));
|
||
}
|
||
|
||
obj.SendRefresh = function () {
|
||
obj.send(String.fromCharCode(0x00, 0x06, 0x00, 0x04));
|
||
}
|
||
|
||
obj.ProcessScreenMsg = function (width, height) {
|
||
if (obj.debugmode > 0) { console.log("ScreenSize: " + width + " x " + height); }
|
||
obj.Canvas.setTransform(1, 0, 0, 1, 0, 0);
|
||
obj.rotation = 0;
|
||
obj.FirstDraw = true;
|
||
obj.ScreenWidth = obj.width = width;
|
||
obj.ScreenHeight = obj.height = height;
|
||
obj.KillDraw = obj.tilesReceived;
|
||
while (obj.PendingOperations.length > 0) { obj.PendingOperations.shift(); }
|
||
obj.SendCompressionLevel(1);
|
||
obj.SendUnPause();
|
||
if (obj.onScreenSizeChange != null) { obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId); }
|
||
}
|
||
|
||
obj.ProcessData = function (str) {
|
||
var ptr = 0;
|
||
//console.log('x0', str.length);
|
||
while (ptr < str.length) {
|
||
//console.log('x1', ptr, str.length);
|
||
ptr += obj.ProcessDataEx(str.substring(ptr));
|
||
//console.log('x2', ptr, str.length);
|
||
}
|
||
}
|
||
|
||
obj.ProcessDataEx = function (str) {
|
||
if (obj.accumulator != null) {
|
||
str = obj.accumulator + str;
|
||
//console.log('KVM using accumulated data, total size is now ' + str.length + ' bytes.');
|
||
obj.accumulator = null;
|
||
}
|
||
if (obj.debugmode > 1) { console.log("KRecv(" + str.length + "): " + rstr2hex(str.substring(0, Math.min(str.length, 40)))); }
|
||
if (str.length < 4) return;
|
||
var cmdmsg = null, X = 0, Y = 0, command = ReadShort(str, 0), cmdsize = ReadShort(str, 2), jumboAdd = 0;
|
||
if ((command == 27) && (cmdsize == 8)) {
|
||
// Jumbo packet
|
||
if (str.length < 12) return;
|
||
command = ReadShort(str, 8)
|
||
cmdsize = ReadInt(str, 4);
|
||
//console.log('JUMBO cmd=' + command + ', cmdsize=' + cmdsize + ', data received=' + str.length);
|
||
if ((cmdsize + 8) > str.length) {
|
||
//console.log('KVM accumulator set to ' + str.length + ' bytes, need ' + cmdsize + ' bytes.');
|
||
obj.accumulator = str;
|
||
return;
|
||
}
|
||
str = str.substring(8);
|
||
jumboAdd = 8;
|
||
}
|
||
if ((cmdsize != str.length) && (obj.debugmode > 0)) { console.log(cmdsize, str.length, cmdsize == str.length); }
|
||
if ((command >= 18) && (command != 65)) { console.error("Invalid KVM command " + command + " of size " + cmdsize); console.log("Invalid KVM data", str.length, rstr2hex(str.substring(0, 40)) + '...'); return; }
|
||
if (cmdsize > str.length) {
|
||
//console.log('KVM accumulator set to ' + str.length + ' bytes, need ' + cmdsize + ' bytes.');
|
||
obj.accumulator = str;
|
||
return;
|
||
}
|
||
//console.log("KVM Command: " + command + " Len:" + cmdsize);
|
||
|
||
if (command == 3 || command == 4 || command == 7) {
|
||
cmdmsg = str.substring(4, cmdsize);
|
||
X = ((cmdmsg.charCodeAt(0) & 0xFF) << 8) + (cmdmsg.charCodeAt(1) & 0xFF);
|
||
Y = ((cmdmsg.charCodeAt(2) & 0xFF) << 8) + (cmdmsg.charCodeAt(3) & 0xFF);
|
||
if (obj.debugmode > 0) { console.log("CMD" + command + " at X=" + X + " Y=" + Y); }
|
||
}
|
||
|
||
switch (command) {
|
||
case 3: // Tile
|
||
if (obj.FirstDraw) obj.onResize();
|
||
obj.ProcessPictureMsg(cmdmsg, X, Y);
|
||
break;
|
||
case 4: // Tile Copy
|
||
if (obj.FirstDraw) obj.onResize();
|
||
if (obj.TilesDrawn == obj.tilesReceived) {
|
||
obj.ProcessCopyRectMsg(cmdmsg);
|
||
} else {
|
||
obj.PendingOperations.push([ ++tilesReceived, 1, cmdmsg ]);
|
||
}
|
||
break;
|
||
case 7: // Screen size
|
||
obj.ProcessScreenMsg(X, Y);
|
||
obj.SendKeyMsgKC(obj.KeyAction.UP, 16); // Shift
|
||
obj.SendKeyMsgKC(obj.KeyAction.UP, 17); // Ctrl
|
||
obj.SendKeyMsgKC(obj.KeyAction.UP, 18); // Alt
|
||
obj.SendKeyMsgKC(obj.KeyAction.UP, 91); // Left-Windows
|
||
obj.SendKeyMsgKC(obj.KeyAction.UP, 92); // Right-Windows
|
||
obj.SendKeyMsgKC(obj.KeyAction.UP, 16); // Shift
|
||
obj.send(String.fromCharCode(0x00, 0x0E, 0x00, 0x04));
|
||
break;
|
||
case 11: // GetDisplays
|
||
var selectedDisplay = 0, displays = { }, dcount = ((str.charCodeAt(4) & 0xFF) << 8) + (str.charCodeAt(5) & 0xFF);
|
||
if (dcount > 0) {
|
||
// Many displays present
|
||
selectedDisplay = ((str.charCodeAt(6 + (dcount * 2)) & 0xFF) << 8) + (str.charCodeAt(7 + (dcount * 2)) & 0xFF);
|
||
for (var i = 0; i < dcount; i++) {
|
||
var disp = ((str.charCodeAt(6 + (i * 2)) & 0xFF) << 8) + (str.charCodeAt(7 + (i * 2)) & 0xFF);
|
||
if (disp == 65535) { displays[disp] = 'All Displays'; } else { displays[disp] = 'Display ' + disp; }
|
||
}
|
||
}
|
||
//console.log('Get Displays', displays, selectedDisplay, rstr2hex(str));
|
||
if (obj.onDisplayinfo != null) { obj.onDisplayinfo(obj, displays, selectedDisplay); }
|
||
break;
|
||
case 12: // SetDisplay
|
||
//console.log('SetDisplayConfirmed');
|
||
break;
|
||
case 14: // KVM_INIT_TOUCH
|
||
obj.touchenabled = 1;
|
||
obj.TouchArray = {};
|
||
if (obj.onTouchEnabledChanged != null) obj.onTouchEnabledChanged(obj.touchenabled);
|
||
break;
|
||
case 15: // KVM_TOUCH
|
||
obj.TouchArray = {};
|
||
break;
|
||
case 16: // MNG_KVM_CONNECTCOUNT
|
||
obj.connectioncount = ReadInt(str, 4);
|
||
//obj.Debug("Got KVM Connect Count: " + obj.connectioncount);
|
||
if (obj.onConnectCountChanged != null) obj.onConnectCountChanged(obj.connectioncount, obj);
|
||
break;
|
||
case 17: // MNG_KVM_MESSAGE
|
||
//obj.Debug("Got KVM Message: " + str.substring(4, cmdsize));
|
||
if (obj.onMessage != null) obj.onMessage(str.substring(4, cmdsize), obj);
|
||
break;
|
||
case 65: // Alert
|
||
str = str.substring(4);
|
||
if (str[0] != '.') {
|
||
console.log(str); //alert('KVM: ' + str);
|
||
obj.parent.consoleMessage = str;
|
||
if (obj.parent.onConsoleMessageChange) { obj.parent.onConsoleMessageChange(obj.parent, str); }
|
||
} else {
|
||
console.log('KVM: ' + str.substring(1));
|
||
}
|
||
break;
|
||
}
|
||
return cmdsize + jumboAdd;
|
||
}
|
||
|
||
// Keyboard and Mouse I/O.
|
||
obj.MouseButton = { "NONE": 0x00, "LEFT": 0x02, "RIGHT": 0x08, "MIDDLE": 0x20 };
|
||
obj.KeyAction = { "NONE": 0, "DOWN": 1, "UP": 2, "SCROLL": 3, "EXUP": 4, "EXDOWN": 5, "DBLCLICK": 6 };
|
||
obj.InputType = { "KEY": 1, "MOUSE": 2, "CTRLALTDEL": 10, "TOUCH": 15 };
|
||
obj.Alternate = 0;
|
||
|
||
var convertKeyCodeTable = {
|
||
"Pause": 19,
|
||
"CapsLock": 20,
|
||
"Space": 32,
|
||
"Quote": 222,
|
||
"Minus": 189,
|
||
"NumpadMultiply": 106,
|
||
"NumpadAdd": 107,
|
||
"PrintScreen": 44,
|
||
"Comma": 188,
|
||
"NumpadSubtract": 109,
|
||
"NumpadDecimal": 110,
|
||
"Period": 190,
|
||
"Slash": 191,
|
||
"NumpadDivide": 111,
|
||
"Semicolon": 186,
|
||
"Equal": 187,
|
||
"OSLeft": 91,
|
||
"BracketLeft": 219,
|
||
"OSRight": 91,
|
||
"Backslash": 220,
|
||
"BracketRight": 221,
|
||
"ContextMenu": 93,
|
||
"Backquote": 192,
|
||
"NumLock": 144,
|
||
"ScrollLock": 145,
|
||
"Backspace": 8,
|
||
"Tab": 9,
|
||
"Enter": 13,
|
||
"NumpadEnter": 13,
|
||
"Escape": 27,
|
||
"Delete": 46,
|
||
"Home": 36,
|
||
"PageUp": 33,
|
||
"PageDown": 34,
|
||
"ArrowLeft": 37,
|
||
"ArrowUp": 38,
|
||
"ArrowRight": 39,
|
||
"ArrowDown": 40,
|
||
"End": 35,
|
||
"Insert": 45,
|
||
"F1": 112,
|
||
"F2": 113,
|
||
"F3": 114,
|
||
"F4": 115,
|
||
"F5": 116,
|
||
"F6": 117,
|
||
"F7": 118,
|
||
"F8": 119,
|
||
"F9": 120,
|
||
"F10": 121,
|
||
"F11": 122,
|
||
"F12": 123,
|
||
"ShiftLeft": 16,
|
||
"ShiftRight": 16,
|
||
"ControlLeft": 17,
|
||
"ControlRight": 17,
|
||
"AltLeft": 18,
|
||
"AltRight": 18,
|
||
"MetaLeft": 91,
|
||
"MetaRight": 92,
|
||
"VolumeMute": 181
|
||
//"LaunchMail":
|
||
//"LaunchApp1":
|
||
//"LaunchApp2":
|
||
//"BrowserStop":
|
||
//"MediaStop":
|
||
//"MediaTrackPrevious":
|
||
//"MediaTrackNext":
|
||
//"MediaPlayPause":
|
||
//"MediaSelect":
|
||
}
|
||
|
||
function convertKeyCode(e) {
|
||
if (e.code.startsWith('Key') && e.code.length == 4) { return e.code.charCodeAt(3); }
|
||
if (e.code.startsWith('Digit') && e.code.length == 6) { return e.code.charCodeAt(5); }
|
||
if (e.code.startsWith('Numpad') && e.code.length == 7) { return e.code.charCodeAt(6) + 48; }
|
||
return convertKeyCodeTable[e.code];
|
||
}
|
||
|
||
obj.SendKeyMsg = function (action, event) {
|
||
if (action == null) return;
|
||
if (!event) { event = window.event; }
|
||
if (event.code && (obj.localKeyMap == false)) {
|
||
// Convert "event.code" into a scancode. This works the same regardless of the keyboard language.
|
||
// Older browsers will not support this.
|
||
var kc = convertKeyCode(event);
|
||
if (kc != null) { obj.SendKeyMsgKC(action, kc); }
|
||
} else {
|
||
// Use this keycode, this works best with "US-EN" keyboards.
|
||
// Older browser support this.
|
||
var kc = event.keyCode;
|
||
if (kc == 0x3B) { kc = 0xBA; } // Fix the ';' key
|
||
obj.SendKeyMsgKC(action, kc);
|
||
}
|
||
}
|
||
|
||
obj.SendMessage = function (msg) {
|
||
if (obj.State == 3) obj.send(String.fromCharCode(0x00, 0x11) + obj.shortToStr(4 + msg.length) + msg); // 0x11 = 17 MNG_KVM_MESSAGE
|
||
}
|
||
|
||
obj.SendKeyMsgKC = function (action, kc) {
|
||
//console.log('SendKeyMsgKC', action, kc);
|
||
if (obj.State != 3) return;
|
||
if (typeof action == 'object') { for (var i in action) { obj.SendKeyMsgKC(action[i][0], action[i][1]); } }
|
||
else { obj.send(String.fromCharCode(0x00, obj.InputType.KEY, 0x00, 0x06, (action - 1), kc)); }
|
||
}
|
||
|
||
obj.sendcad = function() { obj.SendCtrlAltDelMsg(); }
|
||
|
||
obj.SendCtrlAltDelMsg = function () {
|
||
if (obj.State == 3) { obj.send(String.fromCharCode(0x00, obj.InputType.CTRLALTDEL, 0x00, 0x04)); }
|
||
}
|
||
|
||
obj.SendEscKey = function () {
|
||
if (obj.State == 3) obj.send(String.fromCharCode(0x00, obj.InputType.KEY, 0x00, 0x06, 0x00, 0x1B, 0x00, obj.InputType.KEY, 0x00, 0x06, 0x01, 0x1B));
|
||
}
|
||
|
||
obj.SendStartMsg = function () {
|
||
obj.SendKeyMsgKC(obj.KeyAction.EXDOWN, 0x5B); // L-Windows
|
||
obj.SendKeyMsgKC(obj.KeyAction.EXUP, 0x5B); // L-Windows
|
||
}
|
||
|
||
obj.SendCharmsMsg = function () {
|
||
obj.SendKeyMsgKC(obj.KeyAction.EXDOWN, 0x5B); // L-Windows
|
||
obj.SendKeyMsgKC(obj.KeyAction.DOWN, 67); // C
|
||
obj.SendKeyMsgKC(obj.KeyAction.UP, 67); // C
|
||
obj.SendKeyMsgKC(obj.KeyAction.EXUP, 0x5B); // L-Windows
|
||
}
|
||
|
||
obj.SendTouchMsg1 = function (id, flags, x, y) {
|
||
if (obj.State == 3) obj.send(String.fromCharCode(0x00, obj.InputType.TOUCH) + obj.shortToStr(14) + String.fromCharCode(0x01, id) + obj.intToStr(flags) + obj.shortToStr(x) + obj.shortToStr(y));
|
||
}
|
||
|
||
obj.SendTouchMsg2 = function (id, flags) {
|
||
var msg = '';
|
||
var flags2;
|
||
var str = "TOUCHSEND: ";
|
||
for (var k in obj.TouchArray) {
|
||
if (k == id) { flags2 = flags; } else {
|
||
if (obj.TouchArray[k].f == 1) { flags2 = 0x00010000 | 0x00000002 | 0x00000004; obj.TouchArray[k].f = 3; str += "START" + k; } // POINTER_FLAG_DOWN
|
||
else if (obj.TouchArray[k].f == 2) { flags2 = 0x00040000; str += "STOP" + k; } // POINTER_FLAG_UP
|
||
else flags2 = 0x00000002 | 0x00000004 | 0x00020000; // POINTER_FLAG_UPDATE
|
||
}
|
||
msg += String.fromCharCode(k) + obj.intToStr(flags2) + obj.shortToStr(obj.TouchArray[k].x) + obj.shortToStr(obj.TouchArray[k].y);
|
||
if (obj.TouchArray[k].f == 2) delete obj.TouchArray[k];
|
||
}
|
||
if (obj.State == 3) obj.send(String.fromCharCode(0x00, obj.InputType.TOUCH) + obj.shortToStr(5 + msg.length) + String.fromCharCode(0x02) + msg);
|
||
if (Object.keys(obj.TouchArray).length == 0 && obj.touchtimer != null) { clearInterval(obj.touchtimer); obj.touchtimer = null; }
|
||
}
|
||
|
||
obj.SendMouseMsg = function (Action, event) {
|
||
if (obj.State != 3) return;
|
||
if (Action != null && obj.Canvas != null) {
|
||
if (!event) { var event = window.event; }
|
||
|
||
var ScaleFactorHeight = (obj.Canvas.canvas.height / obj.CanvasId.clientHeight);
|
||
var ScaleFactorWidth = (obj.Canvas.canvas.width / obj.CanvasId.clientWidth);
|
||
var Offsets = obj.GetPositionOfControl(obj.Canvas.canvas);
|
||
var X = ((event.pageX - Offsets[0]) * ScaleFactorWidth);
|
||
var Y = ((event.pageY - Offsets[1]) * ScaleFactorHeight);
|
||
if (event.addx) { X += event.addx; }
|
||
if (event.addy) { Y += event.addy; }
|
||
|
||
if (X >= 0 && X <= obj.Canvas.canvas.width && Y >= 0 && Y <= obj.Canvas.canvas.height) {
|
||
var Button = 0;
|
||
var Delta = 0;
|
||
if (Action == obj.KeyAction.UP || Action == obj.KeyAction.DOWN) {
|
||
if (event.which) { ((event.which == 1) ? (Button = obj.MouseButton.LEFT) : ((event.which == 2) ? (Button = obj.MouseButton.MIDDLE) : (Button = obj.MouseButton.RIGHT))); }
|
||
else if (event.button) { ((event.button == 0) ? (Button = obj.MouseButton.LEFT) : ((event.button == 1) ? (Button = obj.MouseButton.MIDDLE) : (Button = obj.MouseButton.RIGHT))); }
|
||
}
|
||
else if (Action == obj.KeyAction.SCROLL) {
|
||
if (event.detail) { Delta = (-1 * (event.detail * 120)); } else if (event.wheelDelta) { Delta = (event.wheelDelta * 3); }
|
||
}
|
||
|
||
var MouseMsg = "";
|
||
if (Action == obj.KeyAction.DBLCLICK) {
|
||
MouseMsg = String.fromCharCode(0x00, obj.InputType.MOUSE, 0x00, 0x0A, 0x00, 0x88, ((X / 256) & 0xFF), (X & 0xFF), ((Y / 256) & 0xFF), (Y & 0xFF));
|
||
} else if (Action == obj.KeyAction.SCROLL) {
|
||
MouseMsg = String.fromCharCode(0x00, obj.InputType.MOUSE, 0x00, 0x0C, 0x00, 0x00, ((X / 256) & 0xFF), (X & 0xFF), ((Y / 256) & 0xFF), (Y & 0xFF), ((Delta / 256) & 0xFF), (Delta & 0xFF));
|
||
} else {
|
||
MouseMsg = String.fromCharCode(0x00, obj.InputType.MOUSE, 0x00, 0x0A, 0x00, ((Action == obj.KeyAction.DOWN) ? Button : ((Button * 2) & 0xFF)), ((X / 256) & 0xFF), (X & 0xFF), ((Y / 256) & 0xFF), (Y & 0xFF));
|
||
}
|
||
|
||
if (obj.Action == obj.KeyAction.NONE) {
|
||
if (obj.Alternate == 0 || obj.ipad) { obj.send(MouseMsg); obj.Alternate = 1; } else { obj.Alternate = 0; }
|
||
} else {
|
||
obj.send(MouseMsg);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
obj.GetDisplayNumbers = function () { obj.send(String.fromCharCode(0x00, 0x0B, 0x00, 0x04)); } // Get Terminal display
|
||
obj.SetDisplay = function (number) { console.log('Set display', number); obj.send(String.fromCharCode(0x00, 0x0C, 0x00, 0x06, number >> 8, number & 0xFF)); } // Set Terminal display
|
||
obj.intToStr = function (x) { return String.fromCharCode((x >> 24) & 0xFF, (x >> 16) & 0xFF, (x >> 8) & 0xFF, x & 0xFF); }
|
||
obj.shortToStr = function (x) { return String.fromCharCode((x >> 8) & 0xFF, x & 0xFF); }
|
||
|
||
obj.onResize = function () {
|
||
if (obj.ScreenWidth == 0 || obj.ScreenHeight == 0) return;
|
||
if (obj.Canvas.canvas.width == obj.ScreenWidth && obj.Canvas.canvas.height == obj.ScreenHeight) return;
|
||
if (obj.FirstDraw) {
|
||
obj.Canvas.canvas.width = obj.ScreenWidth;
|
||
obj.Canvas.canvas.height = obj.ScreenHeight;
|
||
obj.Canvas.fillRect(0, 0, obj.ScreenWidth, obj.ScreenHeight);
|
||
if (obj.onScreenSizeChange != null) obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId);
|
||
}
|
||
obj.FirstDraw = false;
|
||
//obj.Debug("onResize: " + obj.ScreenWidth + " x " + obj.ScreenHeight);
|
||
}
|
||
|
||
obj.xxMouseInputGrab = false;
|
||
obj.xxKeyInputGrab = false;
|
||
obj.xxMouseMove = function (e) { if (obj.State == 3) obj.SendMouseMsg(obj.KeyAction.NONE, e); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
|
||
obj.xxMouseUp = function (e) { if (obj.State == 3) obj.SendMouseMsg(obj.KeyAction.UP, e); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
|
||
obj.xxMouseDown = function (e) { if (obj.State == 3) obj.SendMouseMsg(obj.KeyAction.DOWN, e); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
|
||
obj.xxMouseDblClick = function (e) { if (obj.State == 3) obj.SendMouseMsg(obj.KeyAction.DBLCLICK, e); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
|
||
obj.xxDOMMouseScroll = function (e) { if (obj.State == 3) { obj.SendMouseMsg(obj.KeyAction.SCROLL, e); return false; } return true; }
|
||
obj.xxMouseWheel = function (e) { if (obj.State == 3) { obj.SendMouseMsg(obj.KeyAction.SCROLL, e); return false; } return true; }
|
||
obj.xxKeyUp = function (e) { if (obj.State == 3) { obj.SendKeyMsg(obj.KeyAction.UP, e); } if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
|
||
obj.xxKeyDown = function (e) { if (obj.State == 3) { obj.SendKeyMsg(obj.KeyAction.DOWN, e); } if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
|
||
obj.xxKeyPress = function (e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
|
||
|
||
// Key handlers
|
||
obj.handleKeys = function (e) { if (obj.stopInput == true || desktop.State != 3) return false; return obj.xxKeyPress(e); }
|
||
obj.handleKeyUp = function (e) {
|
||
if (obj.stopInput == true || desktop.State != 3) return false;
|
||
if (obj.firstUpKeys.length < 5) {
|
||
obj.firstUpKeys.push(e.keyCode);
|
||
if ((obj.firstUpKeys.length == 5)) { var j = obj.firstUpKeys.join(','); if ((j == '16,17,91,91,16') || (j == '16,17,18,91,92')) { obj.stopInput = true; } }
|
||
} return obj.xxKeyUp(e);
|
||
}
|
||
obj.handleKeyDown = function (e) { if (obj.stopInput == true || desktop.State != 3) return false; return obj.xxKeyDown(e); }
|
||
|
||
// Mouse handlers
|
||
obj.mousedblclick = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseDblClick(e); }
|
||
obj.mousedown = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseDown(e); }
|
||
obj.mouseup = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseUp(e); }
|
||
obj.mousemove = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseMove(e); }
|
||
obj.mousewheel = function (e) { if (obj.stopInput == true) return false; return obj.xxMouseWheel(e); }
|
||
|
||
obj.xxMsTouchEvent = function (evt) {
|
||
if (evt.originalEvent.pointerType == 4) return; // If this is a mouse pointer, ignore this event. Touch & pen are ok.
|
||
if (evt.preventDefault) evt.preventDefault();
|
||
if (evt.stopPropagation) evt.stopPropagation();
|
||
if (evt.type == 'MSPointerDown' || evt.type == 'MSPointerMove' || evt.type == 'MSPointerUp') {
|
||
var flags = 0;
|
||
var id = evt.originalEvent.pointerId % 256;
|
||
var X = evt.offsetX * (Canvas.canvas.width / obj.CanvasId.clientWidth);
|
||
var Y = evt.offsetY * (Canvas.canvas.height / obj.CanvasId.clientHeight);
|
||
|
||
if (evt.type == 'MSPointerDown') flags = 0x00010000 | 0x00000002 | 0x00000004; // POINTER_FLAG_DOWN
|
||
else if (evt.type == 'MSPointerMove') {
|
||
//if (obj.TouchArray[id] && MuchTheSame(obj.TouchArray[id].x, X) && MuchTheSame(obj.TouchArray[id].y, Y)) return;
|
||
flags = 0x00020000 | 0x00000002 | 0x00000004; // POINTER_FLAG_UPDATE
|
||
}
|
||
else if (evt.type == 'MSPointerUp') flags = 0x00040000; // POINTER_FLAG_UP
|
||
|
||
if (!obj.TouchArray[id]) obj.TouchArray[id] = { x: X, y : Y };
|
||
obj.SendTouchMsg2(id, flags)
|
||
if (evt.type == 'MSPointerUp') delete obj.TouchArray[id];
|
||
} else {
|
||
alert(evt.type);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
obj.xxTouchStart = function (e) {
|
||
if (obj.State != 3) return;
|
||
if (e.preventDefault) e.preventDefault();
|
||
if (obj.touchenabled == 0 || obj.touchenabled == 1) {
|
||
if (e.originalEvent.touches.length > 1) return;
|
||
var t = e.originalEvent.touches[0];
|
||
e.which = 1;
|
||
obj.LastX = e.pageX = t.pageX;
|
||
obj.LastY = e.pageY = t.pageY;
|
||
obj.SendMouseMsg(KeyAction.DOWN, e);
|
||
} else {
|
||
var Offsets = obj.GetPositionOfControl(Canvas.canvas);
|
||
for (var i in e.originalEvent.changedTouches) {
|
||
if (!e.originalEvent.changedTouches[i].identifier) continue;
|
||
var id = e.originalEvent.changedTouches[i].identifier % 256;
|
||
if (!obj.TouchArray[id]) { obj.TouchArray[id] = { x: (e.originalEvent.touches[i].pageX - Offsets[0]) * (Canvas.canvas.width / obj.CanvasId.clientWidth), y: (e.originalEvent.touches[i].pageY - Offsets[1]) * (Canvas.canvas.height / obj.CanvasId.clientHeight), f: 1 }; }
|
||
}
|
||
if (Object.keys(obj.TouchArray).length > 0 && touchtimer == null) { obj.touchtimer = setInterval(function () { obj.SendTouchMsg2(256, 0); }, 50); }
|
||
}
|
||
}
|
||
|
||
obj.xxTouchMove = function (e) {
|
||
if (obj.State != 3) return;
|
||
if (e.preventDefault) e.preventDefault();
|
||
if (obj.touchenabled == 0 || obj.touchenabled == 1) {
|
||
if (e.originalEvent.touches.length > 1) return;
|
||
var t = e.originalEvent.touches[0];
|
||
e.which = 1;
|
||
obj.LastX = e.pageX = t.pageX;
|
||
obj.LastY = e.pageY = t.pageY;
|
||
obj.SendMouseMsg(obj.KeyAction.NONE, e);
|
||
} else {
|
||
var Offsets = obj.GetPositionOfControl(Canvas.canvas);
|
||
for (var i in e.originalEvent.changedTouches) {
|
||
if (!e.originalEvent.changedTouches[i].identifier) continue;
|
||
var id = e.originalEvent.changedTouches[i].identifier % 256;
|
||
if (obj.TouchArray[id]) {
|
||
obj.TouchArray[id].x = (e.originalEvent.touches[i].pageX - Offsets[0]) * (obj.Canvas.canvas.width / obj.CanvasId.clientWidth);
|
||
obj.TouchArray[id].y = (e.originalEvent.touches[i].pageY - Offsets[1]) * (obj.Canvas.canvas.height / obj.CanvasId.clientHeight);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
obj.xxTouchEnd = function (e) {
|
||
if (obj.State != 3) return;
|
||
if (e.preventDefault) e.preventDefault();
|
||
if (obj.touchenabled == 0 || obj.touchenabled == 1) {
|
||
if (e.originalEvent.touches.length > 1) return;
|
||
e.which = 1;
|
||
e.pageX = LastX;
|
||
e.pageY = LastY;
|
||
obj.SendMouseMsg(KeyAction.UP, e);
|
||
} else {
|
||
for (var i in e.originalEvent.changedTouches) {
|
||
if (!e.originalEvent.changedTouches[i].identifier) continue;
|
||
var id = e.originalEvent.changedTouches[i].identifier % 256;
|
||
if (obj.TouchArray[id]) obj.TouchArray[id].f = 2;
|
||
}
|
||
}
|
||
}
|
||
|
||
obj.GrabMouseInput = function () {
|
||
if (obj.xxMouseInputGrab == true) return;
|
||
var c = obj.CanvasId;
|
||
c.onmousemove = obj.xxMouseMove;
|
||
c.onmouseup = obj.xxMouseUp;
|
||
c.onmousedown = obj.xxMouseDown;
|
||
c.touchstart = obj.xxTouchStart;
|
||
c.touchmove = obj.xxTouchMove;
|
||
c.touchend = obj.xxTouchEnd;
|
||
c.MSPointerDown = obj.xxMsTouchEvent;
|
||
c.MSPointerMove = obj.xxMsTouchEvent;
|
||
c.MSPointerUp = obj.xxMsTouchEvent;
|
||
if (navigator.userAgent.match(/mozilla/i)) c.DOMMouseScroll = obj.xxDOMMouseScroll; else c.onmousewheel = obj.xxMouseWheel;
|
||
obj.xxMouseInputGrab = true;
|
||
}
|
||
|
||
obj.UnGrabMouseInput = function () {
|
||
if (obj.xxMouseInputGrab == false) return;
|
||
var c = obj.CanvasId;
|
||
c.onmousemove = null;
|
||
c.onmouseup = null;
|
||
c.onmousedown = null;
|
||
c.touchstart = null;
|
||
c.touchmove = null;
|
||
c.touchend = null;
|
||
c.MSPointerDown = null;
|
||
c.MSPointerMove = null;
|
||
c.MSPointerUp = null;
|
||
if (navigator.userAgent.match(/mozilla/i)) c.DOMMouseScroll = null; else c.onmousewheel = null;
|
||
obj.xxMouseInputGrab = false;
|
||
}
|
||
|
||
obj.GrabKeyInput = function () {
|
||
if (obj.xxKeyInputGrab == true) return;
|
||
document.onkeyup = obj.xxKeyUp;
|
||
document.onkeydown = obj.xxKeyDown;
|
||
document.onkeypress = obj.xxKeyPress;
|
||
obj.xxKeyInputGrab = true;
|
||
}
|
||
|
||
obj.UnGrabKeyInput = function () {
|
||
if (obj.xxKeyInputGrab == false) return;
|
||
document.onkeyup = null;
|
||
document.onkeydown = null;
|
||
document.onkeypress = null;
|
||
obj.xxKeyInputGrab = false;
|
||
}
|
||
|
||
obj.GetPositionOfControl = function (Control) {
|
||
var Position = Array(2);
|
||
Position[0] = Position[1] = 0;
|
||
while (Control) { Position[0] += Control.offsetLeft; Position[1] += Control.offsetTop; Control = Control.offsetParent; }
|
||
return Position;
|
||
}
|
||
|
||
obj.crotX = function (x, y) {
|
||
if (obj.rotation == 0) return x;
|
||
if (obj.rotation == 1) return y;
|
||
if (obj.rotation == 2) return obj.Canvas.canvas.width - x;
|
||
if (obj.rotation == 3) return obj.Canvas.canvas.height - y;
|
||
}
|
||
|
||
obj.crotY = function (x, y) {
|
||
if (obj.rotation == 0) return y;
|
||
if (obj.rotation == 1) return obj.Canvas.canvas.width - x;
|
||
if (obj.rotation == 2) return obj.Canvas.canvas.height - y;
|
||
if (obj.rotation == 3) return x;
|
||
}
|
||
|
||
obj.rotX = function (x, y) {
|
||
if (obj.rotation == 0 || obj.rotation == 1) return x;
|
||
if (obj.rotation == 2) return x - obj.Canvas.canvas.width;
|
||
if (obj.rotation == 3) return x - obj.Canvas.canvas.height;
|
||
}
|
||
|
||
obj.rotY = function (x, y) {
|
||
if (obj.rotation == 0 || obj.rotation == 3) return y;
|
||
if (obj.rotation == 1) return y - obj.Canvas.canvas.width;
|
||
if (obj.rotation == 2) return y - obj.Canvas.canvas.height;
|
||
}
|
||
|
||
obj.tcanvas = null;
|
||
obj.setRotation = function (x) {
|
||
while (x < 0) { x += 4; }
|
||
var newrotation = x % 4;
|
||
if (newrotation == obj.rotation) return true;
|
||
var rw = obj.Canvas.canvas.width;
|
||
var rh = obj.Canvas.canvas.height;
|
||
if (obj.rotation == 1 || obj.rotation == 3) { rw = obj.Canvas.canvas.height; rh = obj.Canvas.canvas.width; }
|
||
|
||
// Copy the canvas, put it back in the correct direction
|
||
if (obj.tcanvas == null) obj.tcanvas = document.createElement('canvas');
|
||
var tcanvasctx = obj.tcanvas.getContext('2d');
|
||
tcanvasctx.setTransform(1, 0, 0, 1, 0, 0);
|
||
tcanvasctx.canvas.width = rw;
|
||
tcanvasctx.canvas.height = rh;
|
||
tcanvasctx.rotate((obj.rotation * -90) * Math.PI / 180);
|
||
if (obj.rotation == 0) tcanvasctx.drawImage(obj.Canvas.canvas, 0, 0);
|
||
if (obj.rotation == 1) tcanvasctx.drawImage(obj.Canvas.canvas, -obj.Canvas.canvas.width, 0);
|
||
if (obj.rotation == 2) tcanvasctx.drawImage(obj.Canvas.canvas, -obj.Canvas.canvas.width, -obj.Canvas.canvas.height);
|
||
if (obj.rotation == 3) tcanvasctx.drawImage(obj.Canvas.canvas, 0, -obj.Canvas.canvas.height);
|
||
|
||
// Change the size and orientation and copy the canvas back into the rotation
|
||
if (obj.rotation == 0 || obj.rotation == 2) { obj.Canvas.canvas.height = rw; obj.Canvas.canvas.width = rh; }
|
||
if (obj.rotation == 1 || obj.rotation == 3) { obj.Canvas.canvas.height = rh; obj.Canvas.canvas.width = rw; }
|
||
obj.Canvas.setTransform(1, 0, 0, 1, 0, 0);
|
||
obj.Canvas.rotate((newrotation * 90) * Math.PI / 180);
|
||
obj.rotation = newrotation;
|
||
obj.Canvas.drawImage(obj.tcanvas, obj.rotX(0, 0), obj.rotY(0, 0));
|
||
|
||
obj.ScreenWidth = obj.Canvas.canvas.width;
|
||
obj.ScreenHeight = obj.Canvas.canvas.height;
|
||
if (obj.onScreenSizeChange != null) obj.onScreenSizeChange(obj, obj.ScreenWidth, obj.ScreenHeight, obj.CanvasId);
|
||
return true;
|
||
}
|
||
|
||
// Private method
|
||
obj.MuchTheSame = function (a, b) { return (Math.abs(a - b) < 4); }
|
||
obj.Debug = function (msg) { console.log(msg); }
|
||
obj.getIEVersion = function () { var r = -1; if (navigator.appName == 'Microsoft Internet Explorer') { var ua = navigator.userAgent; var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); if (re.exec(ua) != null) r = parseFloat(RegExp.$1); } return r; }
|
||
obj.haltEvent = function (e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
|
||
|
||
return obj;
|
||
}
|
||
var QRCode;!function(){function a(a){this.mode=c.MODE_8BIT_BYTE,this.data=a,this.parsedData=[];for(var b=[],d=0,e=this.data.length;e>d;d++){var f=this.data.charCodeAt(d);f>65536?(b[0]=240|(1835008&f)>>>18,b[1]=128|(258048&f)>>>12,b[2]=128|(4032&f)>>>6,b[3]=128|63&f):f>2048?(b[0]=224|(61440&f)>>>12,b[1]=128|(4032&f)>>>6,b[2]=128|63&f):f>128?(b[0]=192|(1984&f)>>>6,b[1]=128|63&f):b[0]=f,this.parsedData=this.parsedData.concat(b)}this.parsedData.length!=this.data.length&&(this.parsedData.unshift(191),this.parsedData.unshift(187),this.parsedData.unshift(239))}function b(a,b){this.typeNumber=a,this.errorCorrectLevel=b,this.modules=null,this.moduleCount=0,this.dataCache=null,this.dataList=[]}function i(a,b){if(void 0==a.length)throw new Error(a.length+"/"+b);for(var c=0;c<a.length&&0==a[c];)c++;this.num=new Array(a.length-c+b);for(var d=0;d<a.length-c;d++)this.num[d]=a[d+c]}function j(a,b){this.totalCount=a,this.dataCount=b}function k(){this.buffer=[],this.length=0}function m(){return"undefined"!=typeof CanvasRenderingContext2D}function n(){var a=!1,b=navigator.userAgent;return/android/i.test(b)&&(a=!0,aMat=b.toString().match(/android ([0-9]\.[0-9])/i),aMat&&aMat[1]&&(a=parseFloat(aMat[1]))),a}function r(a,b){for(var c=1,e=s(a),f=0,g=l.length;g>=f;f++){var h=0;switch(b){case d.L:h=l[f][0];break;case d.M:h=l[f][1];break;case d.Q:h=l[f][2];break;case d.H:h=l[f][3]}if(h>=e)break;c++}if(c>l.length)throw new Error("Too long data");return c}function s(a){var b=encodeURI(a).toString().replace(/\%[0-9a-fA-F]{2}/g,"a");return b.length+(b.length!=a?3:0)}a.prototype={getLength:function(){return this.parsedData.length},write:function(a){for(var b=0,c=this.parsedData.length;c>b;b++)a.put(this.parsedData[b],8)}},b.prototype={addData:function(b){var c=new a(b);this.dataList.push(c),this.dataCache=null},isDark:function(a,b){if(0>a||this.moduleCount<=a||0>b||this.moduleCount<=b)throw new Error(a+","+b);return this.modules[a][b]},getModuleCount:function(){return this.moduleCount},make:function(){this.makeImpl(!1,this.getBestMaskPattern())},makeImpl:function(a,c){this.moduleCount=4*this.typeNumber+17,this.modules=new Array(this.moduleCount);for(var d=0;d<this.moduleCount;d++){this.modules[d]=new Array(this.moduleCount);for(var e=0;e<this.moduleCount;e++)this.modules[d][e]=null}this.setupPositionProbePattern(0,0),this.setupPositionProbePattern(this.moduleCount-7,0),this.setupPositionProbePattern(0,this.moduleCount-7),this.setupPositionAdjustPattern(),this.setupTimingPattern(),this.setupTypeInfo(a,c),this.typeNumber>=7&&this.setupTypeNumber(a),null==this.dataCache&&(this.dataCache=b.createData(this.typeNumber,this.errorCorrectLevel,this.dataList)),this.mapData(this.dataCache,c)},setupPositionProbePattern:function(a,b){for(var c=-1;7>=c;c++)if(!(-1>=a+c||this.moduleCount<=a+c))for(var d=-1;7>=d;d++)-1>=b+d||this.moduleCount<=b+d||(this.modules[a+c][b+d]=c>=0&&6>=c&&(0==d||6==d)||d>=0&&6>=d&&(0==c||6==c)||c>=2&&4>=c&&d>=2&&4>=d?!0:!1)},getBestMaskPattern:function(){for(var a=0,b=0,c=0;8>c;c++){this.makeImpl(!0,c);var d=f.getLostPoint(this);(0==c||a>d)&&(a=d,b=c)}return b},createMovieClip:function(a,b,c){var d=a.createEmptyMovieClip(b,c),e=1;this.make();for(var f=0;f<this.modules.length;f++)for(var g=f*e,h=0;h<this.modules[f].length;h++){var i=h*e,j=this.modules[f][h];j&&(d.beginFill(0,100),d.moveTo(i,g),d.lineTo(i+e,g),d.lineTo(i+e,g+e),d.lineTo(i,g+e),d.endFill())}return d},setupTimingPattern:function(){for(var a=8;a<this.moduleCount-8;a++)null==this.modules[a][6]&&(this.modules[a][6]=0==a%2);for(var b=8;b<this.moduleCount-8;b++)null==this.modules[6][b]&&(this.modules[6][b]=0==b%2)},setupPositionAdjustPattern:function(){for(var a=f.getPatternPosition(this.typeNumber),b=0;b<a.length;b++)for(var c=0;c<a.length;c++){var d=a[b],e=a[c];if(null==this.modules[d][e])for(var g=-2;2>=g;g++)for(var h=-2;2>=h;h++)this.modules[d+g][e+h]=-2==g||2==g||-2==h||2==h||0==g&&0==h?!0:!1}},setupTypeNumber:function(a){for(var b=f.getBCHTypeNumber(this.typeNumber),c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[Math.floor(c/3)][c%3+this.moduleCount-8-3]=d}for(var c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[c%3+this.moduleCount-8-3][Math.floor(c/3)]=d}},setupTypeInfo:function(a,b){for(var c=this.errorCorrectLevel<<3|b,d=f.getBCHTypeInfo(c),e=0;15>e;e++){var g=!a&&1==(1&d>>e);6>e?this.modules[e][8]=g:8>e?this.modules[e+1][8]=g:this.modules[this.moduleCount-15+e][8]=g}for(var e=0;15>e;e++){var g=!a&&1==(1&d>>e);8>e?this.modules[8][this.moduleCount-e-1]=g:9>e?this.modules[8][15-e-1+1]=g:this.modules[8][15-e-1]=g}this.modules[this.moduleCount-8][8]=!a},mapData:function(a,b){for(var c=-1,d=this.moduleCount-1,e=7,g=0,h=this.moduleCount-1;h>0;h-=2)for(6==h&&h--;;){for(var i=0;2>i;i++)if(null==this.modules[d][h-i]){var j=!1;g<a.length&&(j=1==(1&a[g]>>>e));var k=f.getMask(b,d,h-i);k&&(j=!j),this.modules[d][h-i]=j,e--,-1==e&&(g++,e=7)}if(d+=c,0>d||this.moduleCount<=d){d-=c,c=-c;break}}}},b.PAD0=236,b.PAD1=17,b.createData=function(a,c,d){for(var e=j.getRSBlocks(a,c),g=new k,h=0;h<d.length;h++){var i=d[h];g.put(i.mode,4),g.put(i.getLength(),f.getLengthInBits(i.mode,a)),i.write(g)}for(var l=0,h=0;h<e.length;h++)l+=e[h].dataCount;if(g.getLengthInBits()>8*l)throw new Error("code length overflow. ("+g.getLengthInBits()+">"+8*l+")");for(g.getLengthInBits()+4<=8*l&&g.put(0,4);0!=g.getLengthInBits()%8;)g.putBit(!1);for(;;){if(g.getLengthInBits()>=8*l)break;if(g.put(b.PAD0,8),g.getLengthInBits()>=8*l)break;g.put(b.PAD1,8)}return b.createBytes(g,e)},b.createBytes=function(a,b){for(var c=0,d=0,e=0,g=new Array(b.length),h=new Array(b.length),j=0;j<b.length;j++){var k=b[j].dataCount,l=b[j].totalCount-k;d=Math.max(d,k),e=Math.max(e,l),g[j]=new Array(k);for(var m=0;m<g[j].length;m++)g[j][m]=255&a.buffer[m+c];c+=k;var n=f.getErrorCorrectPolynomial(l),o=new i(g[j],n.getLength()-1),p=o.mod(n);h[j]=new Array(n.getLength()-1);for(var m=0;m<h[j].length;m++){var q=m+p.getLength()-h[j].length;h[j][m]=q>=0?p.get(q):0}}for(var r=0,m=0;m<b.length;m++)r+=b[m].totalCount;for(var s=new Array(r),t=0,m=0;d>m;m++)for(var j=0;j<b.length;j++)m<g[j].length&&(s[t++]=g[j][m]);for(var m=0;e>m;m++)for(var j=0;j<b.length;j++)m<h[j].length&&(s[t++]=h[j][m]);return s};for(var c={MODE_NUMBER:1,MODE_ALPHA_NUM:2,MODE_8BIT_BYTE:4,MODE_KANJI:8},d={L:1,M:0,Q:3,H:2},e={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7},f={PATTERN_POSITION_TABLE:[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],G15:1335,G18:7973,G15_MASK:21522,getBCHTypeInfo:function(a){for(var b=a<<10;f.getBCHDigit(b)-f.getBCHDigit(f.G15)>=0;)b^=f.G15<<f.getBCHDigit(b)-f.getBCHDigit(f.G15);return(a<<10|b)^f.G15_MASK},getBCHTypeNumber:function(a){for(var b=a<<12;f.getBCHDigit(b)-f.getBCHDigit(f.G18)>=0;)b^=f.G18<<f.getBCHDigit(b)-f.getBCHDigit(f.G18);return a<<12|b},getBCHDigit:function(a){for(var b=0;0!=a;)b++,a>>>=1;return b},getPatternPosition:function(a){return f.PATTERN_POSITION_TABLE[a-1]},getMask:function(a,b,c){switch(a){case e.PATTERN000:return 0==(b+c)%2;case e.PATTERN001:return 0==b%2;case e.PATTERN010:return 0==c%3;case e.PATTERN011:return 0==(b+c)%3;case e.PATTERN100:return 0==(Math.floor(b/2)+Math.floor(c/3))%2;case e.PATTERN101:return 0==b*c%2+b*c%3;case e.PATTERN110:return 0==(b*c%2+b*c%3)%2;case e.PATTERN111:return 0==(b*c%3+(b+c)%2)%2;default:throw new Error("bad maskPattern:"+a)}},getErrorCorrectPolynomial:function(a){for(var b=new i([1],0),c=0;a>c;c++)b=b.multiply(new i([1,g.gexp(c)],0));return b},getLengthInBits:function(a,b){if(b>=1&&10>b)switch(a){case c.MODE_NUMBER:return 10;case c.MODE_ALPHA_NUM:return 9;case c.MODE_8BIT_BYTE:return 8;case c.MODE_KANJI:return 8;default:throw new Error("mode:"+a)}else if(27>b)switch(a){case c.MODE_NUMBER:return 12;case c.MODE_ALPHA_NUM:return 11;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 10;default:throw new Error("mode:"+a)}else{if(!(41>b))throw new Error("type:"+b);switch(a){case c.MODE_NUMBER:return 14;case c.MODE_ALPHA_NUM:return 13;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 12;default:throw new Error("mode:"+a)}}},getLostPoint:function(a){for(var b=a.getModuleCount(),c=0,d=0;b>d;d++)for(var e=0;b>e;e++){for(var f=0,g=a.isDark(d,e),h=-1;1>=h;h++)if(!(0>d+h||d+h>=b))for(var i=-1;1>=i;i++)0>e+i||e+i>=b||(0!=h||0!=i)&&g==a.isDark(d+h,e+i)&&f++;f>5&&(c+=3+f-5)}for(var d=0;b-1>d;d++)for(var e=0;b-1>e;e++){var j=0;a.isDark(d,e)&&j++,a.isDark(d+1,e)&&j++,a.isDark(d,e+1)&&j++,a.isDark(d+1,e+1)&&j++,(0==j||4==j)&&(c+=3)}for(var d=0;b>d;d++)for(var e=0;b-6>e;e++)a.isDark(d,e)&&!a.isDark(d,e+1)&&a.isDark(d,e+2)&&a.isDark(d,e+3)&&a.isDark(d,e+4)&&!a.isDark(d,e+5)&&a.isDark(d,e+6)&&(c+=40);for(var e=0;b>e;e++)for(var d=0;b-6>d;d++)a.isDark(d,e)&&!a.isDark(d+1,e)&&a.isDark(d+2,e)&&a.isDark(d+3,e)&&a.isDark(d+4,e)&&!a.isDark(d+5,e)&&a.isDark(d+6,e)&&(c+=40);for(var k=0,e=0;b>e;e++)for(var d=0;b>d;d++)a.isDark(d,e)&&k++;var l=Math.abs(100*k/b/b-50)/5;return c+=10*l}},g={glog:function(a){if(1>a)throw new Error("glog("+a+")");return g.LOG_TABLE[a]},gexp:function(a){for(;0>a;)a+=255;for(;a>=256;)a-=255;return g.EXP_TABLE[a]},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)},h=0;8>h;h++)g.EXP_TABLE[h]=1<<h;for(var h=8;256>h;h++)g.EXP_TABLE[h]=g.EXP_TABLE[h-4]^g.EXP_TABLE[h-5]^g.EXP_TABLE[h-6]^g.EXP_TABLE[h-8];for(var h=0;255>h;h++)g.LOG_TABLE[g.EXP_TABLE[h]]=h;i.prototype={get:function(a){return this.num[a]},getLength:function(){return this.num.length},multiply:function(a){for(var b=new Array(this.getLength()+a.getLength()-1),c=0;c<this.getLength();c++)for(var d=0;d<a.getLength();d++)b[c+d]^=g.gexp(g.glog(this.get(c))+g.glog(a.get(d)));return new i(b,0)},mod:function(a){if(this.getLength()-a.getLength()<0)return this;for(var b=g.glog(this.get(0))-g.glog(a.get(0)),c=new Array(this.getLength()),d=0;d<this.getLength();d++)c[d]=this.get(d);for(var d=0;d<a.getLength();d++)c[d]^=g.gexp(g.glog(a.get(d))+b);return new i(c,0).mod(a)}},j.RS_BLOCK_TABLE=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]],j.getRSBlocks=function(a,b){var c=j.getRsBlockTable(a,b);if(void 0==c)throw new Error("bad rs block @ typeNumber:"+a+"/errorCorrectLevel:"+b);for(var d=c.length/3,e=[],f=0;d>f;f++)for(var g=c[3*f+0],h=c[3*f+1],i=c[3*f+2],k=0;g>k;k++)e.push(new j(h,i));return e},j.getRsBlockTable=function(a,b){switch(b){case d.L:return j.RS_BLOCK_TABLE[4*(a-1)+0];case d.M:return j.RS_BLOCK_TABLE[4*(a-1)+1];case d.Q:return j.RS_BLOCK_TABLE[4*(a-1)+2];case d.H:return j.RS_BLOCK_TABLE[4*(a-1)+3];default:return void 0}},k.prototype={get:function(a){var b=Math.floor(a/8);return 1==(1&this.buffer[b]>>>7-a%8)},put:function(a,b){for(var c=0;b>c;c++)this.putBit(1==(1&a>>>b-c-1))},getLengthInBits:function(){return this.length},putBit:function(a){var b=Math.floor(this.length/8);this.buffer.length<=b&&this.buffer.push(0),a&&(this.buffer[b]|=128>>>this.length%8),this.length++}};var l=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]],o=function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){function g(a,b){var c=document.createElementNS("http://www.w3.org/2000/svg",a);for(var d in b)b.hasOwnProperty(d)&&c.setAttribute(d,b[d]);return c}var b=this._htOption,c=this._el,d=a.getModuleCount();Math.floor(b.width/d),Math.floor(b.height/d),this.clear();var h=g("svg",{viewBox:"0 0 "+String(d)+" "+String(d),width:"100%",height:"100%",fill:b.colorLight});h.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xlink","http://www.w3.org/1999/xlink"),c.appendChild(h),h.appendChild(g("rect",{fill:b.colorDark,width:"1",height:"1",id:"template"}));for(var i=0;d>i;i++)for(var j=0;d>j;j++)if(a.isDark(i,j)){var k=g("use",{x:String(i),y:String(j)});k.setAttributeNS("http://www.w3.org/1999/xlink","href","#template"),h.appendChild(k)}},a.prototype.clear=function(){for(;this._el.hasChildNodes();)this._el.removeChild(this._el.lastChild)},a}(),p="svg"===document.documentElement.tagName.toLowerCase(),q=p?o:m()?function(){function a(){this._elImage.src=this._elCanvas.toDataURL("image/png"),this._elImage.style.display="block",this._elCanvas.style.display="none"}function d(a,b){var c=this;if(c._fFail=b,c._fSuccess=a,null===c._bSupportDataURI){var d=document.createElement("img"),e=function(){c._bSupportDataURI=!1,c._fFail&&_fFail.call(c)},f=function(){c._bSupportDataURI=!0,c._fSuccess&&c._fSuccess.call(c)};return d.onabort=e,d.onerror=e,d.onload=f,d.src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",void 0}c._bSupportDataURI===!0&&c._fSuccess?c._fSuccess.call(c):c._bSupportDataURI===!1&&c._fFail&&c._fFail.call(c)}if(this._android&&this._android<=2.1){var b=1/window.devicePixelRatio,c=CanvasRenderingContext2D.prototype.drawImage;CanvasRenderingContext2D.prototype.drawImage=function(a,d,e,f,g,h,i,j){if("nodeName"in a&&/img/i.test(a.nodeName))for(var l=arguments.length-1;l>=1;l--)arguments[l]=arguments[l]*b;else"undefined"==typeof j&&(arguments[1]*=b,arguments[2]*=b,arguments[3]*=b,arguments[4]*=b);c.apply(this,arguments)}}var e=function(a,b){this._bIsPainted=!1,this._android=n(),this._htOption=b,this._elCanvas=document.createElement("canvas"),this._elCanvas.width=b.width,this._elCanvas.height=b.height,a.appendChild(this._elCanvas),this._el=a,this._oContext=this._elCanvas.getContext("2d"),this._bIsPainted=!1,this._elImage=document.createElement("img"),this._elImage.style.display="none",this._el.appendChild(this._elImage),this._bSupportDataURI=null};return e.prototype.draw=function(a){var b=this._elImage,c=this._oContext,d=this._htOption,e=a.getModuleCount(),f=d.width/e,g=d.height/e,h=Math.round(f),i=Math.round(g);b.style.display="none",this.clear();for(var j=0;e>j;j++)for(var k=0;e>k;k++){var l=a.isDark(j,k),m=k*f,n=j*g;c.strokeStyle=l?d.colorDark:d.colorLight,c.lineWidth=1,c.fillStyle=l?d.colorDark:d.colorLight,c.fillRect(m,n,f,g),c.strokeRect(Math.floor(m)+.5,Math.floor(n)+.5,h,i),c.strokeRect(Math.ceil(m)-.5,Math.ceil(n)-.5,h,i)}this._bIsPainted=!0},e.prototype.makeImage=function(){this._bIsPainted&&d.call(this,a)},e.prototype.isPainted=function(){return this._bIsPainted},e.prototype.clear=function(){this._oContext.clearRect(0,0,this._elCanvas.width,this._elCanvas.height),this._bIsPainted=!1},e.prototype.round=function(a){return a?Math.floor(1e3*a)/1e3:a},e}():function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){for(var b=this._htOption,c=this._el,d=a.getModuleCount(),e=Math.floor(b.width/d),f=Math.floor(b.height/d),g=['<table style="border:0;border-collapse:collapse;">'],h=0;d>h;h++){g.push("<tr>");for(var i=0;d>i;i++)g.push('<td style="border:0;border-collapse:collapse;padding:0;margin:0;width:'+e+"px;height:"+f+"px;background-color:"+(a.isDark(h,i)?b.colorDark:b.colorLight)+';"></td>');g.push("</tr>")}g.push("</table>"),c.innerHTML=g.join("");var j=c.childNodes[0],k=(b.width-j.offsetWidth)/2,l=(b.height-j.offsetHeight)/2;k>0&&l>0&&(j.style.margin=l+"px "+k+"px")},a.prototype.clear=function(){this._el.innerHTML=""},a}();QRCode=function(a,b){if(this._htOption={width:256,height:256,typeNumber:4,colorDark:"#000000",colorLight:"#ffffff",correctLevel:d.H},"string"==typeof b&&(b={text:b}),b)for(var c in b)this._htOption[c]=b[c];"string"==typeof a&&(a=document.getElementById(a)),this._android=n(),this._el=a,this._oQRCode=null,this._oDrawing=new q(this._el,this._htOption),this._htOption.text&&this.makeCode(this._htOption.text)},QRCode.prototype.makeCode=function(a){this._oQRCode=new b(r(a,this._htOption.correctLevel),this._htOption.correctLevel),this._oQRCode.addData(a),this._oQRCode.make(),this._el.title=a,this._oDrawing.draw(this._oQRCode),this.makeImage()},QRCode.prototype.makeImage=function(){"function"==typeof this._oDrawing.makeImage&&(!this._android||this._android>=3)&&this._oDrawing.makeImage()},QRCode.prototype.clear=function(){this._oDrawing.clear()},QRCode.CorrectLevel=d}();
|
||
'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]); }
|
||
//localStorage.clear();
|
||
|
||
var args;
|
||
var autoReconnect = true;
|
||
var powerStatetable = ['', 'Powered', 'Sleep', 'Sleep', 'Sleep', 'Hibernating', 'Power off', 'Present'];
|
||
var StatusStrs = ['Disconnected', 'Connecting...', 'Setup...', 'Connected', 'Intel® AMT Connected'];
|
||
var sort = 0;
|
||
var searchFocus = 0;
|
||
var mapSearchFocus = 0;
|
||
var userSearchFocus = 0;
|
||
var consoleFocus = 0;
|
||
var showRealNames = false;
|
||
var meshserver = null;
|
||
var meshes = {};
|
||
var meshcount = 0;
|
||
var nodes = null;
|
||
var filetree = {};
|
||
var userinfo = null;
|
||
var serverinfo = null;
|
||
var events = [];
|
||
var users = null;
|
||
var wssessions = null;
|
||
var nodeShortIdent = 0;
|
||
var desktop;
|
||
var desktopsettings = { encoding: 2, showfocus: false, showmouse: true, showcad: true, quality: 40, scaling: 1024, framerate: 50, localkeymap: false };
|
||
var multidesktopsettings = { quality: 20, scaling: 128, framerate: 1000 };
|
||
var terminal;
|
||
var files;
|
||
var debugLevel = parseInt("{{{debuglevel}}}");
|
||
var features = parseInt("{{{features}}}");
|
||
var sessionTime = parseInt("{{{sessiontime}}}");
|
||
var domain = "{{{domain}}}";
|
||
var domainUrl = "{{{domainurl}}}";
|
||
var authCookie = "{{{authCookie}}}";
|
||
var authCookieRenewTimer = null;
|
||
var multiDesktop = {};
|
||
var multiDesktopFilter = null;
|
||
var serverPublicNamePort = "{{{serverDnsName}}}:{{{serverPublicPort}}}";
|
||
var amtScanResults = null;
|
||
var debugmode = 0;
|
||
var clickOnce = (((features & 256) != 0) && detectClickOnce());
|
||
var attemptWebRTC = ((features & 128) != 0);
|
||
var passRequirements = "{{{passRequirements}}}";
|
||
if (passRequirements != "") { passRequirements = JSON.parse(decodeURIComponent(passRequirements)); }
|
||
var deskAspectRatio = 0;
|
||
try { deskAspectRatio = parseInt(getstore('deskAspectRatio', '0')); } catch (ex) { }
|
||
var uiMode = parseInt(getstore('uiMode', 1));
|
||
var webPageStackMenu = false;
|
||
var webPageFullScreen = true;
|
||
var nightMode = (getstore('_nightMode', '0') == '1');
|
||
var sessionActivity = Date.now();
|
||
|
||
// Console Message Display Timers
|
||
var p11DeskConsoleMsgTimer = null;
|
||
var p12TermConsoleMsgTimer = null;
|
||
var p13FilesConsoleMsgTimer = null;
|
||
|
||
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; }
|
||
}
|
||
|
||
// Check if we are in debug mode
|
||
args = parseUriArgs();
|
||
debugmode = args.debug;
|
||
if (args.webrtc != null) { attemptWebRTC = (args.webrtc == 1); }
|
||
QV('p13AutoConnect', debugmode); // Files
|
||
QV('autoconnectbutton2', debugmode); // Terminal
|
||
QV('autoconnectbutton1', debugmode); // Desktop
|
||
//QV('DeskClip', debugmode); // Clipboard feature, not completed so show in in debug mode only.
|
||
|
||
if (nightMode) { QC('body').add('night'); }
|
||
toggleFullScreen();
|
||
|
||
// Setup page visuals
|
||
if (args.hide) {
|
||
var hide = parseInt(args.hide);
|
||
QV('masthead', !(hide & 1));
|
||
QV('topbar', !(hide & 2));
|
||
QV('footer', !(hide & 4));
|
||
QV('p10title', !(hide & 8));
|
||
QV('p11title', !(hide & 8));
|
||
QV('p12title', !(hide & 8));
|
||
QV('p13title', !(hide & 8));
|
||
QV('p14title', !(hide & 8));
|
||
QV('p15title', !(hide & 8));
|
||
QV('p16title', !(hide & 8));
|
||
//if (hide & 16) {
|
||
// QV('page_leftbar', false);
|
||
// QS('page_content').left = '0px';
|
||
//}
|
||
}
|
||
|
||
// We are looking at a single device, remove all the back buttons
|
||
if ('{{currentNode}}' != '') {
|
||
QV('p10BackButton', false);
|
||
QV('p11BackButton', false);
|
||
QV('p12BackButton', false);
|
||
QV('p13BackButton', false);
|
||
QV('p14BackButton', false);
|
||
QV('p15BackButton', false);
|
||
QV('p16BackButton', false);
|
||
}
|
||
p1updateInfo();
|
||
|
||
// Setup the context menu
|
||
document.onclick = function (e) { hideContextMenu(); }
|
||
document.onkeypress = ondockeypress;
|
||
document.onkeydown = ondockeydown;
|
||
document.onkeyup = ondockeyup;
|
||
window.onresize = function () { masterUpdate(512); }
|
||
setTimeout("masterUpdate(512)", 200);
|
||
|
||
// Connect to the mesh server
|
||
meshserver = MeshServerCreateControl(domainUrl, authCookie);
|
||
meshserver.onStateChanged = onStateChanged;
|
||
meshserver.onMessage = onMessage;
|
||
meshserver.trace = (args.trace == 1);
|
||
meshserver.Start();
|
||
|
||
// Setup page controls
|
||
Q('sortselect').selectedIndex = sort = getstore("sort", 0);
|
||
Q('sizeselect').selectedIndex = getstore("_viewsize", 1);
|
||
Q('SearchInput').value = getstore("_search", "");
|
||
showRealNames = (getstore("showRealNames", 0) == 1);
|
||
Q('RealNameCheckBox').checked = showRealNames;
|
||
Q('viewselect').value = getstore("_deviceView", 1);
|
||
Q('DeskControl').checked = (getstore('DeskControl', 1) == 1);
|
||
|
||
// Display the page devices
|
||
masterUpdate(3)
|
||
for (var j = 1; j < 5; j++) { Q('devViewButton' + j).classList.remove('viewSelectorSel'); }
|
||
Q('devViewButton' + Q('viewselect').value).classList.add('viewSelectorSel');
|
||
|
||
// Setup upload drag & drop
|
||
Q('p5filetable').addEventListener("drop", p5fileDragDrop, false);
|
||
Q('p5filetable').addEventListener("dragover", p5fileDragOver, false);
|
||
Q('p5filetable').addEventListener("dragleave", p5fileDragLeave, false);
|
||
//Q('p5fileCatchAllInput').addEventListener("drop", p5fileDragDrop, false);
|
||
//Q('p5fileCatchAllInput').addEventListener("dragover", p5fileDragOver, false);
|
||
//Q('p5fileCatchAllInput').addEventListener("dragleave", p5fileDragLeave, false);
|
||
|
||
// Setup upload drag & drop
|
||
Q('p13filetable').addEventListener("drop", p13fileDragDrop, false);
|
||
Q('p13filetable').addEventListener("dragover", p13fileDragOver, false);
|
||
Q('p13filetable').addEventListener("dragleave", p13fileDragLeave, false);
|
||
|
||
// Timeline update interval
|
||
setInterval(updateDeviceTimeline, 120000); // Check every 2 minutes
|
||
|
||
// Load desktop settings
|
||
var t = localStorage.getItem('desktopsettings');
|
||
if (t != null) { desktopsettings = JSON.parse(t); }
|
||
t = localStorage.getItem('multidesktopsettings');
|
||
if (t != null) { multidesktopsettings = JSON.parse(t); }
|
||
applyDesktopSettings();
|
||
|
||
// Terminal special keys
|
||
var x = '';
|
||
for (var c = 1; c < 27; c++) x += "<option value='" + c + "'>Ctrl-" + String.fromCharCode(64 + c) + " (" + c + ")</option>";
|
||
QH('specialkeylist', x);
|
||
|
||
// Setup server stats panels
|
||
setupGeneralServerStats();
|
||
setupServerTimelineStats();
|
||
|
||
// Setup the user interface in the right mode
|
||
userInterfaceSelectMenu();
|
||
|
||
// If SSPI or LDAP authentication not used, allow batch account creation.
|
||
QV('p4UserBatchCreate', (features & 0x00080000) == 0);
|
||
}
|
||
|
||
// Toggle the web page to full screen
|
||
function toggleAspectRatio(toggle) {
|
||
if (toggle === 1) { deskAspectRatio = ((deskAspectRatio + 1) % 3); putstore('deskAspectRatio', deskAspectRatio); }
|
||
deskAdjust();
|
||
}
|
||
|
||
// If FullScreen, toggle menu to be horisontal or vertical
|
||
function toggleStackMenu(toggle) {
|
||
if (webPageFullScreen == true) {
|
||
if (toggle === 1) {
|
||
webPageStackMenu = !webPageStackMenu;
|
||
putstore('webPageStackMenu', webPageStackMenu);
|
||
}
|
||
if (webPageStackMenu == false) {
|
||
QC('body').remove("menu_stack");
|
||
} else {
|
||
QC('body').add("menu_stack");
|
||
if (xxcurrentView >= 10) QC('column_l').remove('room4submenu');
|
||
}
|
||
deskAdjust();
|
||
}
|
||
}
|
||
|
||
// Toggle user interface menu
|
||
function showUserInterfaceSelectMenu() {
|
||
Q('uiViewButton1').classList.remove('uiSelectorSel');
|
||
Q('uiViewButton2').classList.remove('uiSelectorSel');
|
||
Q('uiViewButton3').classList.remove('uiSelectorSel');
|
||
Q('uiViewButton4').classList.remove('uiSelectorSel');
|
||
try { Q('uiViewButton' + uiMode).classList.add('uiSelectorSel'); } catch (ex) { }
|
||
QV('uiMenu', (QS('uiMenu').display == 'none'));
|
||
if (nightMode) { Q('uiViewButton4').classList.add('uiSelectorSel'); }
|
||
}
|
||
|
||
function userInterfaceSelectMenu(s) {
|
||
if (s) { uiMode = s; putstore('uiMode', uiMode); }
|
||
webPageFullScreen = (uiMode < 3);
|
||
webPageStackMenu = (uiMode > 1);
|
||
toggleFullScreen(0);
|
||
toggleStackMenu(0);
|
||
if (webPageStackMenu && (xxcurrentView >= 10)) { QC('column_l').add('room4submenu'); } else { QC('column_l').remove('room4submenu'); }
|
||
}
|
||
|
||
function toggleNightMode() {
|
||
nightMode = !nightMode;
|
||
if (nightMode) { QC('body').add('night'); } else { QC('body').remove('night'); }
|
||
putstore('_nightMode', nightMode?'1':'0');
|
||
}
|
||
|
||
// Toggle the web page to full screen
|
||
function toggleFullScreen(toggle) {
|
||
if (toggle === 1) { webPageFullScreen = !webPageFullScreen; putstore('webPageFullScreen', webPageFullScreen); }
|
||
var hide = 0;
|
||
if (args.hide) { hide = parseInt(args.hide); }
|
||
if (webPageFullScreen == false) {
|
||
QC('body').remove("menu_stack");
|
||
QC('body').remove("fullscreen");
|
||
QC('body').remove("arg_hide");
|
||
if (xxcurrentView >= 10) QC('column_l').add('room4submenu');
|
||
QV('UserDummyMenuSpan', false);
|
||
//QV('page_leftbar', false);
|
||
} else {
|
||
QC('body').add("fullscreen");
|
||
if (hide & 16) QC('body').add("arg_hide"); // This is replacement for QV('page_leftbar', !(hide & 16));
|
||
if (xxcurrentView >= 10) QC('column_l').remove('room4submenu');
|
||
QV('UserDummyMenuSpan', (xxcurrentView < 10) && webPageFullScreen);
|
||
}
|
||
masterUpdate(512);
|
||
QV('body', true);
|
||
}
|
||
|
||
function getNodeFromId(id) { if (nodes != null) { for (var i in nodes) { if (nodes[i]._id == id) return nodes[i]; } } return null; }
|
||
function reload() { window.location.href = window.location.href; }
|
||
|
||
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
|
||
|
||
// Clean up
|
||
powerTimeline = null;
|
||
powerTimelineReq = null;
|
||
powerTimelineNode = null;
|
||
powerTimelineUpdate = null;
|
||
deleteAllNotifications(); // Close and clear notifications if present
|
||
hideContextMenu(); // Hide the context menu if present
|
||
QV('verifyEmailId2', false);
|
||
QV('logoutControl', false);
|
||
if (errorCode == 'noauth') { QH('p0span', 'Unable to perform authentication'); return; }
|
||
if (prevState == 2) { if (autoReconnect) { setTimeout(serverPoll, 5000); } } else { QH('p0span', 'Unable to connect web socket'); }
|
||
if (authCookieRenewTimer != null) { clearInterval(authCookieRenewTimer); authCookieRenewTimer = null; }
|
||
} else if (state == 2) {
|
||
// Fetch list of meshes, nodes, files
|
||
meshserver.send({ action: 'meshes' });
|
||
meshserver.send({ action: 'nodes', id: '{{currentNode}}' });
|
||
if ('{{currentNode}}' == '') { meshserver.send({ action: 'files' }); }
|
||
go(1);
|
||
authCookieRenewTimer = setInterval(function () { meshserver.send({ action: 'authcookie' }); }, 1800000); // Request a cookie refresh every 30 minutes.
|
||
}
|
||
}
|
||
|
||
// Poll the server, if it responds, refresh the page.
|
||
function serverPoll() {
|
||
var 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();
|
||
}
|
||
|
||
// Return true if this browser supports clickonce
|
||
function detectClickOnce() {
|
||
for (var i in window.navigator.mimeTypes) { if (window.navigator.mimeTypes[i].type == "application/x-ms-application") { return true; } }
|
||
var userAgent = window.navigator.userAgent.toUpperCase();
|
||
return (userAgent.indexOf('.NET CLR 3.5') >= 0) || (userAgent.indexOf('(WINDOWS NT ') >= 0);
|
||
}
|
||
|
||
function updateSiteAdmin() {
|
||
var noServerBackup = "{{{noServerBackup}}}";
|
||
var siteRights = userinfo.siteadmin;
|
||
if (noServerBackup == 1) { siteRights &= 0xFFFFFFFA; } // If not server backups allowed, remove server backup and restore permissions
|
||
|
||
// Update account actions
|
||
QV('p2AccountSecurity', ((features & 4) == 0) && (serverinfo.domainauth == false) && ((features & 4096) != 0)); // Hide Account Security if in single user mode, domain authentication to 2 factor auth not supported.
|
||
QV('p2AccountActions', ((features & 4) == 0) && (serverinfo.domainauth == false)); // Hide Account Actions if in single user mode or domain authentication
|
||
QV('p2AccountImage', ((features & 4) == 0) && (serverinfo.domainauth == false)); // If account actions are not visible, also remove the image on that panel
|
||
QV('p2ServerActions', siteRights & 21);
|
||
QV('LeftMenuMyServer', siteRights & 21); // 16 + 4 + 1
|
||
QV('MainMenuMyServer', siteRights & 21);
|
||
QV('p2ServerActionsBackup', siteRights & 1);
|
||
QV('p2ServerActionsRestore', siteRights & 4);
|
||
QV('p2ServerActionsVersion', siteRights & 16);
|
||
QV('MainMenuMyFiles', siteRights & 8);
|
||
QV('LeftMenuMyFiles', siteRights & 8);
|
||
if (((siteRights & 8) == 0) && (xxcurrentView == 5)) { setDialogMode(0); go(1); }
|
||
if (currentNode != null) { gotoDevice(currentNode._id, xxcurrentView, true); }
|
||
|
||
// Update user management state
|
||
if ((userinfo.siteadmin & 2) != 0)
|
||
{
|
||
// We are user administrator
|
||
if (users == null) { meshserver.send({ action: 'users' }); }
|
||
if (wssessions == null) { meshserver.send({ action: 'wssessioncount' }); }
|
||
} else {
|
||
// We are not user administrator
|
||
users = null;
|
||
wssessions = null;
|
||
updateUsers();
|
||
if (xxcurrentView == 4 || ((xxcurrentView >= 30) && (xxcurrentView < 40))) { setDialogMode(0); go(1); currentUser = null; }
|
||
}
|
||
meshserver.send({ action: 'events', limit: parseInt(p3limitdropdown.value) });
|
||
QV('p2deleteall', userinfo.siteadmin == 0xFFFFFFFF);
|
||
QV('ServerConsole', userinfo.siteadmin === 0xFFFFFFFF);
|
||
if ((xxcurrentView == 115) && (userinfo.siteadmin != 0xFFFFFFFF)) { go(6); }
|
||
if ((xxcurrentView == 6) && ((userinfo.siteadmin & 21) == 0)) { go(1); }
|
||
|
||
// If we are site administrator, register to get server statistics
|
||
if ((siteRights & 21) != 0) { meshserver.send({ action: 'serverstats', interval: 10000 }); }
|
||
}
|
||
|
||
// To boost the speed of the web page when even floods occur, this method perform a delayed update on the web page.
|
||
var updateNaggleTimer = null;
|
||
var updateNaggleFlags = 0;
|
||
function masterUpdate(flags) {
|
||
updateNaggleFlags |= flags;
|
||
if (updateNaggleTimer == null) {
|
||
updateNaggleTimer = setTimeout(function () {
|
||
if (updateNaggleFlags & 512) { center(); }
|
||
if (updateNaggleFlags & 1) { onSearchInputChanged(); }
|
||
if (updateNaggleFlags & 2) { onSortSelectChange(false); }
|
||
if (updateNaggleFlags & 128) { updateMeshes(); }
|
||
if (updateNaggleFlags & 4) { updateDevices(); }
|
||
if (updateNaggleFlags & 8) { drawNotifications(); }
|
||
if (updateNaggleFlags & 16) { updateMapMarkers(); }
|
||
if (updateNaggleFlags & 32) { eventsUpdate(); }
|
||
if (updateNaggleFlags & 64) { refreshMap(false, true); }
|
||
if (updateNaggleFlags & 256) { drawDeviceTimeline(); }
|
||
if (updateNaggleFlags & 1024) { deviceEventsUpdate(); }
|
||
if (updateNaggleFlags & 2048) { userEventsUpdate(); }
|
||
updateNaggleTimer = null;
|
||
updateNaggleFlags = 0;
|
||
}, 150);
|
||
}
|
||
}
|
||
|
||
function updateSelf() {
|
||
QV('verifyEmailId', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
|
||
QV('verifyEmailId2', (userinfo.emailVerified !== true) && (userinfo.email != null) && (serverinfo.emailcheck == true));
|
||
QV('manageOtp', (userinfo.otpsecret == 1) || (userinfo.otphkeys > 0));
|
||
QV('authAppSetupCheck', userinfo.otpsecret == 1);
|
||
QV('authKeySetupCheck', userinfo.otphkeys > 0);
|
||
QV('authCodesSetupCheck', userinfo.otpkeys > 0);
|
||
masterUpdate(4 + 128);
|
||
|
||
// If we can't create new groups, hide all links that can do that.
|
||
var newGroupsAllowed = ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 64) == 0));
|
||
QV('p2createMeshLink1', newGroupsAllowed);
|
||
QV('p2createMeshLink2', newGroupsAllowed);
|
||
QV('getStarted1', newGroupsAllowed);
|
||
QV('getStarted2', !newGroupsAllowed);
|
||
|
||
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) { QH('p2nextPasswordUpdateTime', ' - Reset in ' + Math.floor(seconds / 60) + ' minute' + addLetterS(Math.floor(seconds / 60)) + '.'); }
|
||
else if (seconds < 86400) { QH('p2nextPasswordUpdateTime', ' - Reset in ' + Math.floor(seconds / 3600) + ' hour' + addLetterS(Math.floor(seconds / 3600)) + '.'); }
|
||
else { QH('p2nextPasswordUpdateTime', ' - Reset in ' + Math.floor(seconds / 86400) + ' day' + addLetterS(Math.floor(seconds / 86400)) + '.'); }
|
||
}
|
||
}
|
||
}
|
||
|
||
function addLetterS(x) { return (x > 1) ? 's' : ''; }
|
||
function setSessionActivity() { sessionActivity = Date.now(); QH('idleTimeoutNotify', ''); }
|
||
function checkIdleSessionTimeout() {
|
||
var delta = (Date.now() - sessionActivity);
|
||
if (delta > serverinfo.timeout) { window.location.href = 'logout'; } else {
|
||
var ds = Math.round((serverinfo.timeout - delta) / 1000);
|
||
if (ds <= 60) {
|
||
QH('idleTimeoutNotify', '<br />' + ds + ' second' + addLetterS(ds) + ' until disconnect');
|
||
} else {
|
||
ds = Math.round(ds / 60);
|
||
if (ds <= 5) { QH('idleTimeoutNotify', '<br />' + ds + ' minute' + addLetterS(ds) + ' until disconnect'); }
|
||
}
|
||
}
|
||
}
|
||
|
||
function onMessage(server, message) {
|
||
switch (message.action) {
|
||
case 'serverstats': {
|
||
updateGeneralServerStats(message);
|
||
break;
|
||
}
|
||
case 'servertimelinestats': {
|
||
setServerTimelineStats(message.events);
|
||
break;
|
||
}
|
||
case 'authcookie': {
|
||
// Got an authentication cookie refresh
|
||
authCookie = message.cookie;
|
||
break;
|
||
}
|
||
case 'serverinfo': {
|
||
serverinfo = message.serverinfo;
|
||
if (serverinfo.timeout) { setInterval(checkIdleSessionTimeout, 10000); checkIdleSessionTimeout(); }
|
||
break;
|
||
}
|
||
case 'userinfo': {
|
||
userinfo = message.userinfo;
|
||
updateSiteAdmin();
|
||
updateSelf();
|
||
break;
|
||
}
|
||
case 'users': {
|
||
users = {};
|
||
for (var m in message.users) { users[message.users[m]._id] = message.users[m]; }
|
||
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]; }
|
||
masterUpdate(4 + 128);
|
||
break;
|
||
}
|
||
case 'files': {
|
||
filetree = setupBackPointers(message.filetree);
|
||
updateFiles();
|
||
d3updatefiles();
|
||
break;
|
||
}
|
||
case 'nodes': {
|
||
nodes = [];
|
||
for (var m in message.nodes) {
|
||
if (!meshes[m]) { console.log('Invalid mesh (1): ' + m); continue; }
|
||
for (var n in message.nodes[m]) {
|
||
if (message.nodes[m][n]._id == null) { console.log('Invalid node (' + n + '): ' + JSON.stringify(message.nodes)); continue; }
|
||
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].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;
|
||
message.nodes[m][n].ip = message.nodes[m][n].ip;
|
||
if (!message.nodes[m][n].icon) message.nodes[m][n].icon = 1;
|
||
message.nodes[m][n].ident = ++nodeShortIdent;
|
||
nodes.push(message.nodes[m][n]);
|
||
}
|
||
}
|
||
masterUpdate(1 | 2 | 4 | 64);
|
||
|
||
if (xxcurrentView == 0) { if ('{{viewmode}}' != '') { go(parseInt('{{viewmode}}')); } else { setDialogMode(0); go(1); } }
|
||
if ('{{currentNode}}' != '') { gotoDevice('{{currentNode}}',parseInt('{{viewmode}}'));}
|
||
break;
|
||
}
|
||
case 'powertimeline': {
|
||
if (message.nodeid != powerTimelineReq) break;
|
||
powerTimelineNode = message.nodeid;
|
||
powerTimeline = message.timeline;
|
||
powerTimelineUpdate = Date.now() + 300000; // Update every 5 minutes
|
||
for (var i in powerTimeline) { if (i % 2 == 1) { powerTimeline[i] = powerTimeline[i] * 1000; } } // Decompress time
|
||
if (currentNode._id == message.nodeid) { masterUpdate(256); }
|
||
break;
|
||
}
|
||
case 'lastconnect': {
|
||
var node = getNodeFromId(message.nodeid);
|
||
if (node != null) {
|
||
node.lastconnect = message.time;
|
||
node.lastaddr = message.addr;
|
||
if ((currentNode._id == node._id) && (Q('MainComputerState').innerHTML == '')) {
|
||
QH('MainComputerState', '<span>Last seen:<br />' + printDateTime(new Date(node.lastconnect)) + '</span>');
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case 'msg': {
|
||
// Check if this is a message from a node
|
||
if (message.nodeid != null) {
|
||
var index = -1;
|
||
if (nodes != null) { for (var i in nodes) { if (nodes[i]._id == message.nodeid) { index = i; break; } } }
|
||
if (index != -1) {
|
||
// Node was found, dispatch the message
|
||
if (message.type == 'console') { p15consoleReceive(nodes[index], message.value); } // This is a console message.
|
||
else if (message.type == 'notify') { // This is a notification message.
|
||
var n = { text: message.value, title: message.title, icon: message.icon };
|
||
if (message.nodeid != null) { n.nodeid = message.nodeid; }
|
||
if (message.tag != null) { n.tag = message.tag; }
|
||
if (message.username != null) { n.username = message.username; }
|
||
addNotification(n);
|
||
} else if (message.type == 'ps') {
|
||
showDeskToolsProcesses(message);
|
||
} else if ((message.type == 'getclip') && (xxdialogTag == 'clipboard') && (currentNode != null) && (currentNode._id == message.nodeid)) {
|
||
Q('d2clipText').value = message.data;
|
||
} else if ((message.type == 'setclip') && (xxdialogTag == 'clipboard') && (currentNode != null) && (currentNode._id == message.nodeid)) {
|
||
// Display success/fail on the clipboard dialog box.
|
||
QH('dlgClipStatus', message.success ? '<span style=color:green>Success</span>' : '<span style=color:red>Failed</span>')
|
||
setTimeout(function () { try { QH('dlgClipStatus', ''); } catch (ex) { } }, 2000);
|
||
}
|
||
}
|
||
} else {
|
||
if (message.type == 'notify') { // This is a notification message.
|
||
var n = { text: message.value, title: message.title, icon: message.icon };
|
||
if (message.tag != null) { n.tag = message.tag; }
|
||
if (message.username != null) { n.username = message.username; }
|
||
addNotification(n);
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case 'getnetworkinfo': {
|
||
if ((currentNode._id == message.nodeid) && (xxdialogMode == 2) && (xxdialogTag == 'if' + message.nodeid)) {
|
||
if (message.netif == null) {
|
||
QH('d2netinfo', 'No network interface information available for this device.');
|
||
} else {
|
||
var x = '<div class=dialogText>';
|
||
|
||
if (currentNode.lastconnect) { x += addHtmlValue2('Last agent connection', printDateTime(new Date(currentNode.lastconnect))); }
|
||
if (currentNode.lastaddr) {
|
||
var splitip = currentNode.lastaddr.split(':');
|
||
if (splitip.length > 2) {
|
||
// IPv6
|
||
x += addHtmlValue2('Last agent address', currentNode.lastaddr);
|
||
} else {
|
||
// IPv4
|
||
if (isPrivateIP(currentNode.lastaddr)) {
|
||
x += addHtmlValue2('Last agent address', splitip[0]);
|
||
} else {
|
||
x += addHtmlValue2('Last agent address', '<a href="https://iplocation.com/?ip=' + splitip[0] + '" rel="noreferrer noopener" target="MeshIPLoopup">' + splitip[0] + '</a>');
|
||
}
|
||
}
|
||
}
|
||
|
||
x += addHtmlValue2('Last interfaces update', printDateTime(new Date(message.updateTime)));
|
||
for (var i in message.netif) {
|
||
var net = message.netif[i];
|
||
x += '<hr />'
|
||
if (net.name) { x += addHtmlValue2('Name', '<b>' + EscapeHtml(net.name) + '</b>'); }
|
||
if (net.desc) { x += addHtmlValue2('Description', EscapeHtml(net.desc).replace('(R)', '®').replace('(r)', '®')); }
|
||
if (net.dnssuffix) { x += addHtmlValue2('DNS suffix', EscapeHtml(net.dnssuffix)); }
|
||
if (net.mac) { x += addHtmlValue2('MAC address', '<a href="https://dnslytics.com/mac-address-lookup/' + net.mac.substring(0,6) + '" rel="noreferrer noopener" target="MeshMACLoopup">' + EscapeHtml(net.mac.toLowerCase()) + '</a>'); }
|
||
if (net.v4addr) { x += addHtmlValue2('IPv4 address', EscapeHtml(net.v4addr)); }
|
||
if (net.v4mask) { x += addHtmlValue2('IPv4 mask', EscapeHtml(net.v4mask)); }
|
||
if (net.v4gateway) { x += addHtmlValue2('IPv4 gateway', EscapeHtml(net.v4gateway)); }
|
||
if (net.gatewaymac) { x += addHtmlValue2('Gateway MAC', '<a href="https://dnslytics.com/mac-address-lookup/' + net.gatewaymac.substring(0, 6) + '" rel="noreferrer noopener" target="MeshMACLoopup">' + EscapeHtml(net.gatewaymac.toLowerCase()) + '</a>'); }
|
||
}
|
||
x += '</div>';
|
||
QH('d2netinfo', x);
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case 'serverversion': {
|
||
if ((xxdialogMode == 2) && (xxdialogTag == 'MeshCentralServerUpdate')) {
|
||
var x = '<div class=dialogText>';
|
||
if (!message.current) { message.current = 'Unknown'; }
|
||
if (!message.latest) { message.latest = 'Unknown'; }
|
||
x += addHtmlValue2('Current Version', '<b>' + EscapeHtml(message.current) + '</b>');
|
||
x += addHtmlValue2('Latest Version', '<b>' + EscapeHtml(message.latest) + '</b>');
|
||
x += '</div>';
|
||
if ((message.latest.indexOf('.') == -1) || (message.current == message.latest) || ((features & 2048) == 0)) {
|
||
setDialogMode(2, "MeshCentral Version", 1, null, x);
|
||
} else {
|
||
setDialogMode(2, "MeshCentral Version", 3, server_showVersionDlgEx, x + '<br /><label><input id=d2updateCheck type=checkbox onclick=server_showVersionDlgUpdate() /> Check and click OK to start server self-update.</label>');
|
||
server_showVersionDlgUpdate();
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case 'servererrors': {
|
||
if ((xxdialogMode == 2) && (xxdialogTag == 'MeshCentralServerErrors')) {
|
||
if (message.data == null) {
|
||
setDialogMode(2, "MeshCentral Server Errors", 1, null, 'Server has no error log.');
|
||
} else {
|
||
var x = '<div class="dialogText dialogTextLog"><pre>' + message.data + '<pre></div>';
|
||
setDialogMode(2, "MeshCentral Server Errors", 3, server_showErrorsDlgEx, x + '<br /><label><input id=d2updateCheck type=checkbox onclick=server_showVersionDlgUpdate() /> Check and click OK to clear error log.</label>');
|
||
server_showVersionDlgUpdate();
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case 'serverconsole': {
|
||
p15consoleReceive('serverconsole', message.value);
|
||
break;
|
||
}
|
||
case 'events': {
|
||
if ((message.nodeid != null) && (message.nodeid == currentNode._id)) {
|
||
currentDeviceEvents = message.events;
|
||
masterUpdate(1024);
|
||
} else if ((message.user != null) && (message.user == currentUser.name)) {
|
||
currentUserEvents = message.events;
|
||
masterUpdate(2048);
|
||
} else {
|
||
events = message.events;
|
||
masterUpdate(32);
|
||
}
|
||
break;
|
||
}
|
||
case 'getcookie': {
|
||
if (message.tag == 'clickonce') {
|
||
var basicPort = "{{{serverRedirPort}}}" == "" ? "{{{serverPublicPort}}}" : "{{{serverRedirPort}}}";
|
||
var rdpurl = "http://" + window.location.hostname + ":" + basicPort + "/clickonce/minirouter/MeshMiniRouter.application?WS=wss%3A%2F%2F" + window.location.hostname + "%2Fmeshrelay.ashx%3Fauth=" + message.cookie + "&CH={{{webcerthash}}}&AP=" + message.protocol + ((debugmode == 1) ? "" : "&HOL=1");
|
||
var newWindow = window.open(rdpurl, '_blank');
|
||
newWindow.opener = null;
|
||
}
|
||
break;
|
||
}
|
||
case 'getNotes': {
|
||
var n = Q('d2devNotes');
|
||
if (n && (message.id == decodeURIComponent(n.attributes['noteid'].value))) {
|
||
if (message.notes) { QH('d2devNotes', decodeURIComponent(message.notes)); } else { QH('d2devNotes', ''); }
|
||
var ro = (n.attributes['ro'].value == 'true');
|
||
if (ro == false) { // If we have permissions, set read/write on this note.
|
||
n.removeAttribute('readonly');
|
||
QE('idx_dlgOkButton', true);
|
||
QV('idx_dlgOkButton', true);
|
||
focusTextBox('d2devNotes');
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case 'otpauth-request': {
|
||
if ((xxdialogMode == 2) && (xxdialogTag == 'otpauth-request')) {
|
||
var secret = message.secret;
|
||
if (secret.length == 52) { secret = secret.split(/(.............)/).filter(Boolean).join(' '); }
|
||
else if (secret.length == 32) { secret = secret.split(/(....)/).filter(Boolean).join(' '); secret = secret.substring(0, 20) + '<br/>' + secret.substring(20) }
|
||
QH('d2optinfo', '<table style=width:380px><tr><td style=vertical-align:top>Install <a href=\"https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2\" rel=\"noreferrer noopener\" target=_blank>Google Authenticator</a> or a compatible application and scan the barcode, use <a href=\"' + message.url + '\" rel=\"noreferrer noopener\" target=_blank> this link</a> or enter the secret. Then, enter the current 6 digit token below to activate 2-Step login.<br /><br />Secret<br /><tt id=d2optsecret secret=\"' + message.secret + '\" style=font-size:12px>' + secret + '</tt><br /><br /></td><td style=width:1px;vertical-align:top><a href=\"' + message.url + '\" rel=\"noreferrer noopener\" target=_blank><div id="qrcode"></div></a></td><tr><td colspan=2 style="text-align:center;border-top:1px solid black"><br />Enter the token here for 2-step login: <input type=text onkeypress=\"return (event.keyCode == 8) || (event.charCode >= 48 && event.charCode <= 57)\" onkeyup=account_addOtpCheck(event) onkeydown=account_addOtpCheck() maxlength=6 id=d2otpauthinput type=text></td></table>');
|
||
new QRCode(Q("qrcode"), { text: message.url, width: 128, height: 128, colorDark: "#000000", colorLight: "#EEE", correctLevel: QRCode.CorrectLevel.H });
|
||
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>Authenticator app activation successful</b>. You will now need a valid token to login again." : "<b style=color:red>2-step login activation failed</b>. Clear the secret from the application and try again. You only have a few minutes to enter the proper code.");
|
||
break;
|
||
}
|
||
case 'otpauth-clear': {
|
||
if (xxdialogMode) return;
|
||
setDialogMode(2, "Authenticator App", 1, null, message.success ? "<b>Authenticator application removed</b>. You can reactivate this feature at any time." : "<b style=color:red>2-step login activation removal failed</b>. Try again.");
|
||
break;
|
||
}
|
||
case 'otpauth-getpasswords': {
|
||
if (xxdialogMode) return;
|
||
var x = "One time tokens can be used as secondary authentication. Generate a set, print them and keep them in a safe place.";
|
||
x += "<div style='border-radius:6px;border: 2px dashed #888;width:100%;margin-top:8px'><div style='padding:8px;font-family:Arial, Helvetica, sans-serif;font-size:20px;font-weight:bold'><table style=width:100%;text-align:center>";
|
||
if (message.passwords) {
|
||
var j = 0;
|
||
for (var i in message.passwords) {
|
||
if (++j % 2) { x += '<tr>'; }
|
||
var p = '' + message.passwords[i].p;
|
||
while (p.length < 8) { p = '0' + p; }
|
||
if (message.passwords[i].u === true) { x += '<td>' + p.substring(0, 4) + ' ' + p.substring(4); } else { x += '<td><strike style=color:#BBB>' + p.substring(0, 4) + ' ' + p.substring(4); + '</strike>'; }
|
||
}
|
||
} else {
|
||
x += '<tr><td>No Active Tokens';
|
||
}
|
||
x += "</table></div></div><br />";
|
||
x += "<div><input type=button value='Close' onclick=setDialogMode(0) style=float:right></input>";
|
||
x += "<input type=button value='Generate New Tokens' onclick='account_manageOtp(1);'></input>";
|
||
if (message.passwords != null) { x += "<input type=button value='Clear Tokens' onclick='account_manageOtp(2);'></input>"; }
|
||
x += "</div><br />";
|
||
setDialogMode(2, "Manage Backup Codes", 8, null, x, 'otpauth-manage');
|
||
break;
|
||
}
|
||
case 'otp-hkey-get': {
|
||
if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) return;
|
||
var start = "<div style='border-radius:6px;border:2px solid #CCC;background-color:#BBB;width:100%;box-sizing:border-box;margin-bottom:6px'><div style='margin:3px;font-family:Arial, Helvetica, sans-serif;font-size:16px;font-weight:bold'><table style=width:100%;text-align:left>";
|
||
var end = "</table></div></div>";
|
||
var x = "<a href='https://www.yubico.com/' rel='noreferrer noopener' target='_blank'>Hardware keys</a> are used as secondary login authentication.";
|
||
x += "<div style='max-height:150px;overflow-y:auto;overflow-x:hidden;margin-top:6px;margin-bottom:6px'>";
|
||
if (message.keys && message.keys.length > 0) {
|
||
for (var i in message.keys) {
|
||
var key = message.keys[i], type = (key.type == 2)?'OTP':'WebAuthn';
|
||
x += start + '<tr style=margin:5px><td style=width:30px><img width=24 height=18 src="images/hardware-key-' + type + '-24.png" style=margin-top:4px><td style=width:250px>' + key.name + "<td><input type=button value='Remove' onclick=account_removehkey(" + key.i + ")></input>" + end;
|
||
}
|
||
} else {
|
||
x += start + '<tr style=text-align:center><td>No Keys Configured' + end;
|
||
}
|
||
x += "</div>";
|
||
x += "<div><input type=button value='Close' onclick=setDialogMode(0) style=float:right></input>";
|
||
if ((features & 0x00020000) != 0) { x += "<input id=d2addkey3 type=button value='Add Key' onclick='account_addhkey(3);'></input>"; }
|
||
if ((features & 0x00004000) != 0) { x += "<input id=d2addkey2 type=button value='Add YubiKey® OTP' onclick='account_addhkey(2);'></input>"; }
|
||
x += "</div><br />";
|
||
setDialogMode(2, "Manage Security Keys", 8, null, x, 'otpauth-hardware-manage');
|
||
if (u2fSupported() == false) { QE('d2addkey1', false); }
|
||
break;
|
||
}
|
||
case 'otp-hkey-yubikey-add': {
|
||
if (message.result) {
|
||
meshserver.send({ action: 'otp-hkey-get' }); // Success, ask for the full list of keys.
|
||
} else {
|
||
setDialogMode(2, "Add Security Key", 1, null, '<br />Error, Unable to add key.<br /><br />');
|
||
}
|
||
break;
|
||
}
|
||
case 'otp-hkey-setup-response': {
|
||
if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) return;
|
||
if (message.result == true) {
|
||
meshserver.send({ action: 'otp-hkey-get' }); // Success, ask for the full list of keys.
|
||
} else {
|
||
setDialogMode(2, "Add Security Key", 1, null, '<br />ERROR: Unable to add key.<br /><br />', 'otpauth-hardware-manage');
|
||
}
|
||
break;
|
||
}
|
||
case 'webauthn-startregister': {
|
||
if (xxdialogMode && (xxdialogTag != 'otpauth-hardware-manage')) return;
|
||
var x = "Press the key button now.<br /><br /><div style=width:100%;text-align:center><img width=120 height=117 src='images/hardware-keypress-120.png' /></div><input id=dp1keyname style=display:none value=" + message.name + " />";
|
||
setDialogMode(2, "Add Security Key", 2, null, x);
|
||
|
||
var publicKey = message.request;
|
||
message.request.challenge = Uint8Array.from(atob(message.request.challenge), function (c) { return c.charCodeAt(0) })
|
||
message.request.user.id = Uint8Array.from(atob(message.request.user.id), function (c) { return c.charCodeAt(0) })
|
||
navigator.credentials.create({ publicKey: publicKey })
|
||
.then(function(newCredentialInfo) {
|
||
// Public key credential
|
||
var r = { rawId: btoa(String.fromCharCode.apply(null, new Uint8Array(newCredentialInfo.rawId))), response: { attestationObject: btoa(String.fromCharCode.apply(null, new Uint8Array(newCredentialInfo.response.attestationObject))), clientDataJSON: btoa(String.fromCharCode.apply(null, new Uint8Array(newCredentialInfo.response.clientDataJSON))) }, type: newCredentialInfo.type };
|
||
meshserver.send({ action: 'webauthn-endregister', response: r });
|
||
setDialogMode(0);
|
||
}, function(error) {
|
||
// Error
|
||
setDialogMode(2, "Add Security Key", 1, null, "ERROR: " + error);
|
||
});
|
||
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
|
||
masterUpdate(32);
|
||
}
|
||
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 oldShowRealNames = localStorage.getItem('showRealNames');
|
||
var oldUiMode = localStorage.getItem('uiMode');
|
||
var oldSort = localStorage.getItem('sort');
|
||
|
||
var webstate = JSON.parse(message.event.state);
|
||
for (var i in webstate) { localStorage.setItem(i, webstate[i]); }
|
||
|
||
// Update the web page
|
||
if ((webstate.deskAspectRatio != null) && (webstate.deskAspectRatio != deskAspectRatio)) { deskAspectRatio = webstate.deskAspectRatio; deskAdjust(); }
|
||
if ((webstate.showRealNames != null) && (webstate.showRealNames != oldShowRealNames)) { showRealNames = Q('RealNameCheckBox').checked = (webstate.showRealNames == "1"); masterUpdate(6); }
|
||
if ((webstate.uiMode != null) && (webstate.uiMode != oldUiMode)) { userInterfaceSelectMenu(parseInt(webstate.uiMode)); }
|
||
if ((webstate.sort != null) && (webstate.sort != oldSort)) { document.getElementById("sortselect").selectedIndex = sort = parseInt(webstate.sort); masterUpdate(6); }
|
||
}
|
||
break;
|
||
}
|
||
case 'servertimelinestats': { addServerTimelineStats(message.event.data); break; }
|
||
case 'accountcreate':
|
||
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' }); }
|
||
var oldgroups = userinfo.groups;
|
||
userinfo = message.event.account;
|
||
if (oldsiteadmin != newsiteadmin) updateSiteAdmin();
|
||
updateSelf();
|
||
|
||
if ((userinfo.siteadmin & 2) != 0) {
|
||
// Compare our groups
|
||
var og = oldgroups ? oldgroups : [];
|
||
var ng = userinfo.groups ? userinfo.groups : [];
|
||
if (og.join(',') != ng.join(',')) {
|
||
// Our groups have changed, re-ask for a list of users.
|
||
users = wssessions = null;
|
||
meshserver.send({ action: 'users' });
|
||
meshserver.send({ action: 'wssessioncount' });
|
||
}
|
||
}
|
||
}
|
||
if (users == null) break;
|
||
|
||
// Check if the account is part of our user group
|
||
if ((userinfo.groups == null) || (userinfo.groups.length == 0) || (findOne(message.event.account.groups, userinfo.groups) == true)) {
|
||
users[message.event.account._id] = message.event.account; // Part of our groups, update this user.
|
||
} else {
|
||
delete users[message.event.account._id]; // No longer part of our groups, remove this user.
|
||
}
|
||
|
||
updateUsers();
|
||
break;
|
||
}
|
||
case 'accountremove': {
|
||
// An account was removed
|
||
if (users == null) break;
|
||
delete users['user/' + domain + '/' + message.event.username.toLowerCase()];
|
||
updateUsers();
|
||
break;
|
||
}
|
||
case 'createmesh': {
|
||
// A new mesh was created
|
||
if ((meshes[message.event.meshid] == null) && (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 };
|
||
masterUpdate(4 + 128);
|
||
meshserver.send({ action: 'files' });
|
||
}
|
||
break;
|
||
}
|
||
case 'meshchange': {
|
||
// Update mesh information
|
||
if (meshes[message.event.meshid] == null) {
|
||
// This is a new mesh for us
|
||
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 (message.event.name != null) { meshes[message.event.meshid].name = message.event.name; }
|
||
if (message.event.desc != null) { meshes[message.event.meshid].desc = message.event.desc; }
|
||
if (message.event.flags != null) { meshes[message.event.meshid].flags = message.event.flags; }
|
||
if (message.event.consent != null) { meshes[message.event.meshid].consent = message.event.consent; }
|
||
if (message.event.links) { meshes[message.event.meshid].links = message.event.links; }
|
||
if (message.event.amt) { meshes[message.event.meshid].amt = message.event.amt; }
|
||
|
||
// Check if we lost rights to this mesh in this change.
|
||
if (meshes[message.event.meshid].links[userinfo._id] == null) {
|
||
if ((xxcurrentView == 20) && (currentMesh == meshes[message.event.meshid])) go(2);
|
||
delete meshes[message.event.meshid];
|
||
|
||
// 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;
|
||
|
||
// If we are looking at a node in the deleted mesh, move back to "My Devices"
|
||
if (xxcurrentView >= 10 && xxcurrentView < 20 && currentNode && currentNode.meshid == message.event.meshid) { setDialogMode(0); go(1); }
|
||
}
|
||
}
|
||
masterUpdate(4 + 128);
|
||
if (currentNode && (currentNode.meshid == message.event.meshid)) { currentNode = null; if ((xxcurrentView >= 10) && (xxcurrentView < 20)) { go(1); } }
|
||
//meshserver.send({ action: 'files' }); // TODO: Why do we need to do this??
|
||
|
||
// 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];
|
||
masterUpdate(128);
|
||
meshserver.send({ action: 'files' });
|
||
}
|
||
|
||
// Delete all nodes in that mesh
|
||
var newnodes = [];
|
||
if (nodes != null) { for (var i in nodes) { if (nodes[i].meshid != message.event.meshid) { newnodes.push(nodes[i]); } } }
|
||
nodes = newnodes;
|
||
masterUpdate(4);
|
||
|
||
// If we are looking at a mesh that is now deleted, move back to "My Account"
|
||
if (xxcurrentView >= 20 && xxcurrentView < 30 && currentMesh._id == message.event.meshid) { setDialogMode(0); go(2); }
|
||
// If we are looking at a node in the deleted mesh, move back to "My Devices"
|
||
if (xxcurrentView >= 10 && xxcurrentView < 20 && currentNode && currentNode.meshid == message.event.meshid) { setDialogMode(0); go(1); }
|
||
|
||
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].name.toLowerCase();
|
||
node.state = 0;
|
||
if (!node.icon) node.icon = 1;
|
||
node.ident = ++nodeShortIdent;
|
||
if (nodes == null) { }
|
||
nodes.push(node);
|
||
|
||
// Web page update
|
||
masterUpdate(1 | 2 | 4 | 16);
|
||
|
||
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(1); }
|
||
currentNode = null;
|
||
// TODO: Correctly disconnect from this node (Desktop/Terminal/Files...)
|
||
}
|
||
nodes.splice(index, 1);
|
||
|
||
// Web page update
|
||
masterUpdate(4 | 16);
|
||
}
|
||
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.users = message.event.node.users;
|
||
node.host = message.event.node.host;
|
||
node.desc = message.event.node.desc;
|
||
node.ip = message.event.node.ip;
|
||
node.osdesc = message.event.node.osdesc;
|
||
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;
|
||
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.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.state != null) { node.intelamt.state = message.event.node.intelamt.state; }
|
||
}
|
||
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; }
|
||
|
||
// Web page update
|
||
masterUpdate(2 | 4 | 8 | 16);
|
||
refreshDevice(node._id);
|
||
|
||
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) {
|
||
// We don't see the new mesh, remove this device
|
||
|
||
// TODO: Correctly disconnect from this node (Desktop/Terminal/Files...)
|
||
if (currentNode == node) { if (xxcurrentView >= 10 && xxcurrentView < 20) { setDialogMode(0); go(1); } currentNode = null; }
|
||
nodes.splice(index, 1);
|
||
masterUpdate(4 | 16);
|
||
} else {
|
||
// We see the new mesh, move this device
|
||
node.meshid = message.event.newMeshId;
|
||
node.meshnamel = meshes[message.event.newMeshId].name.toLowerCase();
|
||
masterUpdate(1 | 2 | 4);
|
||
}
|
||
refreshDevice(message.event.nodeid);
|
||
} else {
|
||
// This is a new device, add it.
|
||
var node = message.event.node;
|
||
if (!meshes[node.meshid]) break; // This is a node for a mesh we don't know. Happens when we are site administrator, we get all messages.
|
||
node.namel = node.name.toLowerCase();
|
||
if (node.rname) { node.rnamel = node.rname.toLowerCase(); } else { node.rnamel = node.namel; }
|
||
node.meshnamel = meshes[node.meshid].name.toLowerCase();
|
||
node.state = 0;
|
||
if (!node.icon) node.icon = 1;
|
||
node.ident = ++nodeShortIdent;
|
||
if (nodes == null) { }
|
||
nodes.push(node);
|
||
|
||
// Web page update
|
||
masterUpdate(1 | 2 | 4 | 16);
|
||
}
|
||
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;
|
||
|
||
// Web page update
|
||
masterUpdate(4 | 16);
|
||
refreshDevice(node._id);
|
||
}
|
||
break;
|
||
}
|
||
case 'wssessioncount': {
|
||
// Update the active web socket session count for a user
|
||
if (wssessions != null) {
|
||
if (message.event.count == 0 && wssessions['user/' + domain + '/' + message.event.username.toLowerCase()]) {
|
||
delete wssessions['user/' + domain + '/' + message.event.username.toLowerCase()];
|
||
} else {
|
||
wssessions['user/' + domain + '/' + message.event.username.toLowerCase()] = message.event.count;
|
||
}
|
||
updateUsers();
|
||
}
|
||
break;
|
||
}
|
||
case 'clearevents': {
|
||
events = [];
|
||
masterUpdate(32);
|
||
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 = Math.floor(new Date(message.event.time).getTime() / 1000);
|
||
}
|
||
break;
|
||
}
|
||
case 'scanamtdevice': {
|
||
// Populate the Intel AMT scan dialog box with the result of the RMCP scan
|
||
if ((xxdialogMode == null) || (!Q('dp1range')) || (Q('dp1range').value != message.event.range)) return;
|
||
var x = '';
|
||
if (message.event.results == null) {
|
||
// The scan could not occur because of an error. Likely the user range was invalid.
|
||
x = '<div style=width:100%;text-align:center;margin-top:12px>Unable to scan this address range.</div><div style=width:100%;text-align:center;margin-top:12px;color:gray;line-height:1.5>Sample IP range values<br />192.168.0.100<br />192.168.1.0/24<br />192.167.0.1-192.168.0.100</div>';
|
||
} else {
|
||
// Go thru all the results and populate the dialog box
|
||
amtScanResults = message.event.results;
|
||
for (var i in message.event.results) {
|
||
var r = message.event.results[i], shortname = r.hostname;
|
||
if (shortname.length > 20) { shortname = shortname.substring(0, 20) + '...'; }
|
||
var str = '<b title="' + EscapeHtml(r.hostname) + '">' + EscapeHtml(shortname) + '</b> - v' + r.ver;
|
||
if (r.state == 2) { if (r.tls == 1) { str += ' with TLS.'; } else { str += ' without TLS.'; } } else { str += ' not activated.'; }
|
||
x += '<div style=width:100%;margin-bottom:2px;background-color:lightgray><div style=padding:4px><div style=display:inline-block;margin-right:5px><input class=DevScanCheckbox name=dp1checkbox tag="' + EscapeHtml(i) + '" type=checkbox onclick=addAmtScanToMeshCheckbox() /></div><div class=j1 style=display:inline-block></div><div style=display:inline-block;margin-left:5px;overflow-x:auto;white-space:nowrap>' + str + '</div></div></div>';
|
||
}
|
||
// If no results where found, display a nice message
|
||
if (x == '') { x = '<div style=width:100%;text-align:center;margin-top:12px>Scan returned no results.</div><div style=width:100%;text-align:center;margin-top:12px;color:gray;line-height:1.5>Sample IP range values<br />192.168.0.100<br />192.168.1.0/24<br />192.167.0.1-192.168.0.100</div>'; }
|
||
}
|
||
// Set the html in the dialog box and re-enable the scan button
|
||
QH('dp1results', x);
|
||
QE('dp1range', true);
|
||
QE('dp1rangebutton', true);
|
||
break;
|
||
}
|
||
case 'notify': {
|
||
var n = { text: message.event.value, title: message.event.title, icon: message.event.icon };
|
||
if (message.event.tag != null) { n.tag = message.event.tag; }
|
||
addNotification(n);
|
||
break;
|
||
}
|
||
case 'stopped': { // Server is stopping.
|
||
// Disconnect
|
||
//console.log(message.msg);
|
||
break;
|
||
}
|
||
default:
|
||
//console.log('Unknown message.event.action', message.event.action);
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
case 'createInviteLink': { // Agent installation invitation link
|
||
if (xxdialogTag != message.meshid) break;
|
||
var servername = serverinfo.name;
|
||
if ((servername.indexOf('.') == -1) || ((features & 2) != 0)) { servername = window.location.hostname; } // If the server name is not set or it's in LAN-only mode, use the URL hostname as server name.
|
||
var domainUrlNoSlash = domainUrl.substring(0, domainUrl.length - 1);
|
||
var url;
|
||
if (serverinfo.https == true) {
|
||
var portStr = (serverinfo.port == 443) ? '' : (":" + serverinfo.port);
|
||
url = "https://" + servername + portStr + domainUrl + "agentinvite?c=" + message.cookie;
|
||
} else {
|
||
var portStr = (serverinfo.port == 80) ? '' : (":" + serverinfo.port);
|
||
url = "http://" + servername + portStr + domainUrl + "agentinvite?c=" + message.cookie;
|
||
}
|
||
Q('agentInvitationLink').href = url;
|
||
var t = message.expire + ' hour' + addLetterS(message.expire);
|
||
if (message.expire == 24) { t = '1 day'; }
|
||
if (message.expire == 168) { t = '1 week'; }
|
||
if (message.expire == 5040) { t = '1 month'; }
|
||
if (message.expire == 0) { t = 'Unlimited'; }
|
||
QH('agentInvitationLink', 'Invitation Link (' + t + ')');
|
||
QV('agentInvitationLinkDiv', true);
|
||
break;
|
||
}
|
||
case 'stopped': { // Server is stopping.
|
||
// Disconnect
|
||
autoReconnect = false;
|
||
QH('p0span', message.msg);
|
||
break;
|
||
}
|
||
default:
|
||
console.log('Unknown message.action', message.action);
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// MY DEVICES
|
||
//
|
||
|
||
function onRealNameCheckBox() {
|
||
showRealNames = Q('RealNameCheckBox').checked;
|
||
putstore("showRealNames", showRealNames ? 1 : 0);
|
||
masterUpdate(6);
|
||
return;
|
||
}
|
||
|
||
function onDeviceViewChange(i) {
|
||
if (i != null) { Q('viewselect').value = i; }
|
||
for (var j = 1; j < 5; j++) { Q('devViewButton' + j).classList.remove('viewSelectorSel'); }
|
||
Q('devViewButton' + Q('viewselect').value).classList.add('viewSelectorSel');
|
||
putstore("_deviceView", Q('viewselect').value);
|
||
putstore("_viewsize", Q('sizeselect').value);
|
||
masterUpdate(4);
|
||
setTimeout("masterUpdate(512)", 200);
|
||
}
|
||
|
||
function ondockeypress(e) {
|
||
setSessionActivity();
|
||
if (!xxdialogMode && xxcurrentView == 11 && desktop && Q("DeskControl").checked) {
|
||
// Check what keys we are allows to send
|
||
if (currentNode != null) {
|
||
var mesh = meshes[currentNode.meshid];
|
||
var meshrights = mesh.links[userinfo._id].rights;
|
||
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);
|
||
}
|
||
if (!xxdialogMode && xxcurrentView == 12 && terminal && terminal.State == 3) { return terminal.m.TermHandleKeys(e); }
|
||
if (!xxdialogMode && ((xxcurrentView == 15) || (xxcurrentView == 115))) return agentConsoleHandleKeys(e);
|
||
if (!xxdialogMode && xxcurrentView == 4) {
|
||
if (e.ctrlKey == true || e.altKey == true || e.metaKey == true) return;
|
||
var processed = 0;
|
||
if (e.key) {
|
||
if (e.key.length === 1 && userSearchFocus == 0) { Q('UserSearchInput').value = ((Q('UserSearchInput').value + e.key)); processed = 1; }
|
||
if (e.keyCode == 8 && userSearchFocus == 0) { var x = Q('UserSearchInput').value; Q('UserSearchInput').value = x.substring(0, x.length - 1); processed = 1; }
|
||
if (e.keyCode == 27) { Q('UserSearchInput').value = ''; processed = 1; }
|
||
} else {
|
||
if (e.charCode != 0 && userSearchFocus == 0) { Q('UserSearchInput').value = ((Q('UserSearchInput').value + String.fromCharCode(e.charCode))); processed = 1; }
|
||
}
|
||
if (processed > 0) { if (processed == 1) { onUserSearchInputChanged(); } return haltEvent(e); }
|
||
}
|
||
if (xxdialogMode || xxcurrentView != 1) return;
|
||
if (e.ctrlKey == true && e.charCode == 96) {
|
||
showRealNames = !showRealNames;
|
||
Q('RealNameCheckBox').value = showRealNames;
|
||
putstore("showRealNames", showRealNames ? 1 : 0);
|
||
masterUpdate(6)
|
||
return;
|
||
}
|
||
if (e.ctrlKey == true || e.altKey == true || e.metaKey == true) return;
|
||
if (Q('viewselect').value < 3) {
|
||
var processed = 0;
|
||
if (e.key) {
|
||
if (e.key.length === 1 && searchFocus == 0) { Q('SearchInput').value = ((Q('SearchInput').value + e.key)); processed = 1; }
|
||
if (e.keyCode == 8 && searchFocus == 0) { var x = Q('SearchInput').value; Q('SearchInput').value = x.substring(0, x.length - 1); processed = 1; }
|
||
if (e.keyCode == 27) { Q('SearchInput').value = ''; processed = 1; }
|
||
} else {
|
||
if (e.charCode != 0 && searchFocus == 0) { Q('SearchInput').value = ((Q('SearchInput').value + String.fromCharCode(e.charCode))); processed = 1; }
|
||
}
|
||
if (processed > 0) { if (processed == 1) { masterUpdate(5); } return haltEvent(e); }
|
||
}
|
||
if (Q('viewselect').value == 3) {
|
||
if (e.key) {
|
||
if (e.key.length === 1 && mapSearchFocus == 0) { Q('mapSearchLocation').value = ((Q('mapSearchLocation').value + e.key)); processed = 1; }
|
||
//if (e.keyCode == 8 && mapSearchFocus == 0) { var x = Q('mapSearchLocation').value; Q('mapSearchLocation').value = x.substring(0, x.length - 1); processed = 1; }
|
||
if (e.keyCode == 27) { Q('mapSearchLocation').value = ''; mapCloseSearchWindow(); processed = 1; }
|
||
if (e.keyCode == 13) { getSearchLocation(); }
|
||
} else {
|
||
if (e.charCode != 0 && mapSearchFocus == 0) { Q('mapSearchLocation').value = ((Q('mapSearchLocation').value + String.fromCharCode(e.charCode))); processed = 1; }
|
||
}
|
||
}
|
||
}
|
||
|
||
function ondockeydown(e) {
|
||
setSessionActivity();
|
||
if (!xxdialogMode && xxcurrentView == 11 && desktop && Q("DeskControl").checked) {
|
||
// Check what keys we are allows to send
|
||
if (currentNode != null) {
|
||
var mesh = meshes[currentNode.meshid];
|
||
var meshrights = mesh.links[userinfo._id].rights;
|
||
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);
|
||
}
|
||
if (!xxdialogMode && xxcurrentView == 12 && terminal && terminal.State == 3) { terminal.m.TermHandleKeyDown(e); if ((e.keyCode >= 37) && (e.keyCode <= 40)) { haltEvent(e); } }
|
||
if (!xxdialogMode && xxcurrentView == 13 && e.keyCode == 116 && p13filetree != null) { haltEvent(e); return false; } // F5 Refresh on files
|
||
if (!xxdialogMode && ((xxcurrentView == 15) || (xxcurrentView == 115))) { return agentConsoleHandleKeys(e); }
|
||
if (!xxdialogMode && xxcurrentView == 4) {
|
||
if (e.keyCode === 8 && userSearchFocus == 0) { var x = Q('UserSearchInput').value; Q('UserSearchInput').value = (x.substring(0, x.length - 1)); processed = 1; }
|
||
if (e.keyCode === 27) { Q('UserSearchInput').value = ''; processed = 1; }
|
||
if (processed > 0) { if (processed == 1) { masterUpdate(5); } return haltEvent(e); }
|
||
}
|
||
if (xxdialogMode || xxcurrentView != 1 || e.ctrlKey == true || e.altKey == true || e.metaKey == true) return;
|
||
var processed = 0;
|
||
if (Q('viewselect').value < 3) {
|
||
if (e.keyCode === 8 && searchFocus == 0) { var x = Q('SearchInput').value; Q('SearchInput').value = (x.substring(0, x.length - 1)); processed = 1; }
|
||
if (e.keyCode === 27) { Q('SearchInput').value = ''; processed = 1; }
|
||
if (processed > 0) { if (processed == 1) { masterUpdate(5); } return haltEvent(e); }
|
||
}
|
||
if (Q('viewselect').value == 3) {
|
||
if (e.keyCode === 8 && mapSearchFocus == 0) { var x = Q('mapSearchLocation').value; Q('mapSearchLocation').value = (x.substring(0, x.length - 1)); processed = 1; }
|
||
if (e.keyCode === 27) { Q('mapSearchLocation').value = ''; mapCloseSearchWindow(); processed = 1; }
|
||
}
|
||
}
|
||
|
||
function ondockeyup(e) {
|
||
setSessionActivity();
|
||
if (!xxdialogMode && xxcurrentView == 11 && desktop && Q("DeskControl").checked) {
|
||
// Check what keys we are allows to send
|
||
if (currentNode != null) {
|
||
var mesh = meshes[currentNode.meshid];
|
||
var meshrights = mesh.links[userinfo._id].rights;
|
||
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);
|
||
}
|
||
if (!xxdialogMode && xxcurrentView == 12 && terminal && terminal.State == 3) { return terminal.m.TermHandleKeyUp(e); }
|
||
if (!xxdialogMode && xxcurrentView == 13 && e.keyCode == 116 && p13filetree != null) { p13folderup(9999); haltEvent(e); return false; } // F5 Refresh on files
|
||
if (!xxdialogMode && xxcurrentView == 4) { if ((e.keyCode === 8 && searchFocus == 0) || e.keyCode === 27) { return haltEvent(e); } }
|
||
if (xxdialogMode && e.keyCode == 27) { dialogclose(0); }
|
||
if (xxdialogMode || xxcurrentView != 0 || e.ctrlKey == true || e.altKey == true || e.metaKey == true) return;
|
||
if (Q('viewselect').value < 3) { if ((e.keyCode === 8 && searchFocus == 0) || e.keyCode === 27) { return haltEvent(e); } }
|
||
if (Q('viewselect').value == 3) { if ((e.keyCode === 8 && mapSearchFocus == 0) || e.keyCode === 27) { return haltEvent(e); } }
|
||
}
|
||
|
||
// Highlights the device being hovered
|
||
function devMouseHover(element, over) {
|
||
setSessionActivity();
|
||
var view = Q('viewselect').value;
|
||
if (view == 1) {
|
||
var e = element.children[1].children[1];
|
||
e.children[0].classList.remove('g1s');
|
||
e.children[1].classList.remove('e2s');
|
||
e.children[2].classList.remove('g2s');
|
||
if (over == 1) {
|
||
e.children[0].classList.add('g1s');
|
||
e.children[1].classList.add('e2s');
|
||
e.children[2].classList.add('g2s');
|
||
}
|
||
} else if (view == 2) {
|
||
var e = element;
|
||
e.children[2].classList.remove('g1s');
|
||
e.children[4].classList.remove('e2s');
|
||
e.children[3].classList.remove('g2s');
|
||
if (over == 1) {
|
||
e.children[2].classList.add('g1s');
|
||
e.children[4].classList.add('e2s');
|
||
e.children[3].classList.add('g2s');
|
||
}
|
||
}
|
||
}
|
||
|
||
var deviceHeaderId = 0;
|
||
var deviceHeaderTotal = 0;
|
||
var deviceHeadersTitles = {};
|
||
var deviceHeaderCount;
|
||
var deviceHeaders = {};
|
||
var oldviewmode = 0;
|
||
function updateDevices() {
|
||
if (nodes == null) { return; }
|
||
var r = '', c = 0, current = null, count = 0, displayedMeshes = {}, view = Q('viewselect').value, groups = {}, groupCount = {};
|
||
QV('xdevices', view < 4);
|
||
QV('xdevicesmap', view == 4);
|
||
QV('devListToolbar', view < 3);
|
||
QV('kvmListToolbar', view == 3);
|
||
QV('devMapToolbar', view == 4);
|
||
QV('devListToolbarSize', view == 3);
|
||
QV('NoMeshesPanel', meshcount == 0);
|
||
//QV('devListToolbarView', (meshcount != 0) && (nodes.length > 0));
|
||
QV('devListToolbarViewIcons', (meshcount != 0) && (nodes.length > 0));
|
||
QV('devListToolbarSort', (meshcount != 0) && (nodes.length > 0) && (view < 4));
|
||
if ((meshcount == 0) || (nodes.length == 0)) { view = 1; sort = 0; }
|
||
if (view == 4) {
|
||
setTimeout( function() { if (xxmap.map != null) { xxmap.map.updateSize(); } }, 200);
|
||
// TODO
|
||
} else {
|
||
// 3 wide, list view or desktop view
|
||
deviceHeaderId = 0;
|
||
deviceHeaderCount = {};
|
||
deviceHeaderTotal = 0;
|
||
deviceHeaders = {};
|
||
deviceHeadersTitles = {};
|
||
var kvmDivs = [];
|
||
|
||
// 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); } }
|
||
|
||
// Save the list of currently checked nodeid's
|
||
var checkedNodeids = [], elements = document.getElementsByClassName("DeviceCheckbox"), checkcount = 0;
|
||
for (var i=0;i<elements.length;i++) { if (elements[i].checked) { checkedNodeids.push(elements[i].value); } }
|
||
if ((oldviewmode < 3) && (view == 3)) { multiDesktopFilter = checkedNodeids; }
|
||
else if ((oldviewmode == 3) && (view < 3)) { checkedNodeids = multiDesktopFilter; }
|
||
|
||
// Compute the width of the device view.
|
||
var totalDeviceViewWidth = Q('column_l').clientWidth - 60;
|
||
var deviceBoxWidth = Math.floor(totalDeviceViewWidth / 301);
|
||
deviceBoxWidth = 301 + Math.floor((totalDeviceViewWidth - (deviceBoxWidth * 301)) / deviceBoxWidth);
|
||
|
||
if (view == 2) {
|
||
r += '<table style=width:100%;margin-top:4px cellpadding=0 cellspacing=0><th style=color:gray><th style=color:gray;width:120px>User<th style=color:gray;width:120px>Address<th style=color:gray;width:100px>Connectivity'; //<th style=color:gray;width:100px>State';
|
||
}
|
||
|
||
// Go thru the list of nodes and display them
|
||
for (var i in nodes) {
|
||
var node = nodes[i];
|
||
if (node.v == false) continue;
|
||
var mesh2 = meshes[node.meshid], meshlinks = mesh2.links[userinfo._id];
|
||
if (meshlinks == null) continue;
|
||
var meshrights = meshlinks.rights;
|
||
if ((view == 3) && (mesh2.mtype == 1)) continue;
|
||
if (sort == 0) {
|
||
// Mesh header
|
||
if (node.meshid != current) {
|
||
deviceHeaderSet();
|
||
var extra = '';
|
||
if (view == 2) { r += '<tr><td colspan=5>'; }
|
||
if (meshes[node.meshid].mtype == 1) { extra = '<span class=devHeaderx>, Intel® AMT only</span>'; }
|
||
if ((view == 1) && (current != null)) { if (c == 2) { r += '<td><div style=width:301px></div></td>'; } if (r != '') { r += '</tr></table>'; } }
|
||
if (view == 2) { r += '<div>'; }
|
||
r += '<div class=DevSt style=width:100%;padding-top:4px><span style=float:right>';
|
||
r += '<span id=DevxHeader' + deviceHeaderId + ' class=devHeaderx></span>' + extra;
|
||
r += '</span><span id=MxMESH style=cursor:pointer onclick=gotoMesh("' + node.meshid + '")>' + EscapeHtml(meshes[node.meshid].name) + '</span>' + getMeshActions(mesh2, meshrights) + '</div>';
|
||
if (view == 2) { r += '</div>'; }
|
||
current = node.meshid;
|
||
displayedMeshes[current] = 1;
|
||
c = 0;
|
||
}
|
||
} else if (sort == 1) {
|
||
// Power header
|
||
var pwr = node.pwr?node.pwr:0;
|
||
if (pwr !== current) {
|
||
deviceHeaderSet();
|
||
if ((view == 1) && (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 id=DevxHeader' + deviceHeaderId + ' class=devHeaderx style=float:right></span><span>' + PowerStateStr2(node.pwr) + '</span></div>';
|
||
current = pwr;
|
||
c = 0;
|
||
}
|
||
} else if (sort == 2) {
|
||
// Device header
|
||
if (current == null) { current = '1'; }
|
||
}
|
||
|
||
count++;
|
||
var title = EscapeHtml(node.name);
|
||
if (title.length == 0) { title = '<i>None</i>'; }
|
||
if ((node.rname != null) && (node.rname.length > 0)) { title += " / " + EscapeHtml(node.rname); }
|
||
var name = EscapeHtml(node.name);
|
||
if (showRealNames == true && node.rname != null) name = EscapeHtml(node.rname);
|
||
if (name.length == 0) { name = '<i>None</i>'; }
|
||
|
||
// Node
|
||
var icon = node.icon;
|
||
if ((!node.conn) || (node.conn == 0)) { icon += ' gray'; }
|
||
if (view == 1) {
|
||
r += '<div id=devs onmouseover=devMouseHover(this,1) onmouseout=devMouseHover(this,0) style=display:inline-block;width:' + deviceBoxWidth + 'px;height:50px;padding-top:1px;padding-bottom:1px><div style=width:22px;height:50%;float:left;padding-top:12px><input class="' + node.meshid + ' DeviceCheckbox" onclick=p1updateInfo() value=devid_' + node._id + ' type=checkbox></div><div style=height:100%;cursor:pointer onclick=gotoDevice(\'' + node._id + '\',null,null,event)><div class="i' + icon + '" style=width:50px;float:left></div><div style=height:100%><div class=g1></div><div class=e2><div class=e1 style=width:' + (deviceBoxWidth - 100) + 'px title="' + title + '">' + name + '</div><div>' + NodeStateStr(node) + '</div></div><div class=g2></div></div></div></div>';
|
||
} else if (view == 2) {
|
||
var states = [];
|
||
if (node.conn) {
|
||
if ((node.conn & 1) != 0) { states.push('<span title="Mesh agent is connected and ready for use.">Agent</span>'); }
|
||
if ((node.conn & 2) != 0) { states.push('<span title="Intel® AMT CIRA is connected and ready for use.">CIRA</span>'); }
|
||
else if ((node.conn & 4) != 0) { states.push('<span title="Intel® AMT is routable.">AMT</span>'); }
|
||
if ((node.conn & 8) != 0) { states.push('<span title="Mesh agent is reachable using another agent as relay.">Relay</span>'); }
|
||
}
|
||
r += '<tr><td><div id=devs class=bar18 onmouseover=devMouseHover(this,1) onmouseout=devMouseHover(this,0) style=height:18px;width:100%;font-size:medium>';
|
||
r += '<div class=deviceBarCheckbox><input class="' + node.meshid + ' DeviceCheckbox" onclick=p1updateInfo() value=devid_' + node._id + ' type=checkbox></div>';
|
||
r += '<div class=deviceBarIcon onclick=gotoDevice(\'' + node._id + '\',null,null,event)><div class=\"j' + icon + '\" style=width:16px;margin-top:1px;margin-left:2px;height:16px></div></div>';
|
||
r += '<div class=g1 style=height:18px;float:left></div><div class=g2 style=height:18px;float:right></div>';
|
||
r += '<div style=cursor:pointer;font-size:14px title="' + title + '" onclick=gotoDevice(\'' + node._id + '\',null,null,event)><span style=width:300px>' + name + '</span></div></div></td>';
|
||
r += '<td style=text-align:center>' + getUserShortStr(node);
|
||
r += '<td style=text-align:center>' + (node.ip != null ? node.ip : '');
|
||
r += '<td style=text-align:center>' + states.join(' + ');
|
||
//r += '<td style=text-align:center>' + (node.pwr != null ? powerStateStrings[node.pwr] : '');
|
||
r += '</tr>';
|
||
} else if ((view == 3) && (node.conn & 1) && (((meshrights & 8) || (meshrights & 256)) != 0) && ((node.agent.caps & 1) != 0)) { // Check if we have rights and agent is capable of KVM.
|
||
if ((multiDesktopFilter.length == 0) || (multiDesktopFilter.indexOf('devid_' + node._id) >= 0)) {
|
||
r += '<div id=devs style=display:inline-block;margin:1px;background-color:lightgray;border-radius:5px;position:relative><div style=padding:3px;cursor:pointer onclick=gotoDevice(\'' + node._id + '\',11,null,event)>';
|
||
//r += '<input class="' + node.meshid + ' DeviceCheckbox" onclick=p1updateInfo() value=devid_' + node._id + ' type=checkbox style=float:left>';
|
||
r += '<div class="j' + icon + '" style=width:16px;float:left></div> ' + name + '</div>';
|
||
r += '<span onclick=gotoDevice(\'' + node._id + '\',null,null,event)></span><div id=xkvmid_' + node._id.split('/')[2] + '><div id=skvmid_' + node._id.split('/')[2] + ' style="position:absolute;color:white;left:5px;top:27px;text-shadow:0px 0px 5px #000;z-index:1000;cursor:default" onclick=toggleKvmDevice(\'' + node._id + '\')>Disconnected</div></div>';
|
||
r += '</div>';
|
||
kvmDivs.push(node._id);
|
||
}
|
||
}
|
||
|
||
// If we are displaying devices by group, put the device in the right group.
|
||
if ((sort == 3) && (r != '')) {
|
||
if (node.tags) {
|
||
for (var j in node.tags) {
|
||
var tag = node.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[node.state] == 'undefined') { deviceHeaderCount[node.state] = 1; } else { deviceHeaderCount[node.state]++; }
|
||
}
|
||
|
||
// If displaying devices by groups, sort the group names and display the devices.
|
||
if (sort == 3) {
|
||
var groupNames = [];
|
||
for (var i in groups) { groupNames.push(i); }
|
||
groupNames.sort(function (a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); });
|
||
for (var j in groupNames) {
|
||
var i = groupNames[j]; r += '<div class=DevSt style=width:100%;padding-top:4px><span class=devHeaderx style=float:right>' + groupCount[i] + ' node' + ((groupCount[i] > 1) ? 's' : '') + '</span><span>' + i + '</span></div>' + groups[i];
|
||
}
|
||
}
|
||
|
||
// If there is nothing to display, explain the problem
|
||
if ((r == '') && (meshcount > 0) && (Q('SearchInput').value != '')) {
|
||
if (sort == 3) {
|
||
r = '<div style="margin:30px">No devices are included in any groups, click on a device\'s \"Groups\" to add to a group.</div>';
|
||
} else {
|
||
r = '<div style="margin:30px">No devices matching this search.</div>';
|
||
}
|
||
}
|
||
|
||
if ((view == 1) && (c == 2)) r += '<td><div style=width:301px></div></td>'; // Adds device padding
|
||
|
||
// 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 == '') && (view < 3)) {
|
||
for (var i in meshes) {
|
||
var mesh = meshes[i], meshlink = mesh.links[userinfo._id];
|
||
if (meshlink != null) {
|
||
var meshrights = meshlink.rights;
|
||
if (displayedMeshes[mesh._id] == null) {
|
||
if ((current != '') && (r != '')) { r += '</tr></table>'; }
|
||
r += '<table style=width:100%;padding-top:4px cellpadding=0 cellspacing=0><tr><td colspan=3 class=DevSt><span id=MxMESH style=cursor:pointer onclick=gotoMesh("' + mesh._id + '")>' + EscapeHtml(mesh.name) + '</span><span>';
|
||
r += getMeshActions(mesh, meshrights);
|
||
r += '</span></td></tr><tr>';
|
||
if (mesh.mtype == 1) {
|
||
r += '<td><div style=padding:10px><i>No Intel® AMT devices in this mesh';
|
||
if ((meshrights & 4) != 0) { r += ', <a style=cursor:pointer onclick=addDeviceToMesh(\"' + mesh._id + '\")>add one</a>'; }
|
||
}
|
||
if (mesh.mtype == 2) {
|
||
r += '<td><div style=padding:10px><i>No devices in this mesh';
|
||
if ((meshrights & 4) != 0) { r += ', <a style=cursor:pointer onclick=addAgentToMesh(\"' + mesh._id + '\")>add one</a>'; }
|
||
}
|
||
r += '.</i></div></td>';
|
||
current = mesh._id;
|
||
count++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
r += '</tr></table><div style=height:1px></div>'; // This height of 1 div fixes a problem in Linux firefox browsers
|
||
|
||
// Add a "Add Device Group" option
|
||
r += '<div style=border-top-style:solid;border-top-width:1px;border-top-color:#DDDDDD;cursor:pointer;font-size:10px>';
|
||
if ((view < 3) && (sort == 0) && (meshcount > 0) && ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 64) == 0))) {
|
||
r += '<a onclick=account_createMesh() title="Create a new group of devices." style=cursor:pointer>Add Device Group</a> ';
|
||
}
|
||
if ((userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.siteadmin & 128) == 0)) {
|
||
r += '<a onclick=p10showMeshCmdDialog(0) style=cursor:pointer title="Download MeshCmd, a command line tool that performs many functions.">MeshCmd</a> ';
|
||
if (navigator.platform.toLowerCase() == 'win32') { r += '<a onclick=p10showMeshRouterDialog() style=cursor:pointer title="Download MeshCentral Router, a TCP port mapping tool.">Router</a> '; }
|
||
}
|
||
r += '</div><br/>';
|
||
|
||
QH('xdevices', r);
|
||
deviceHeaderSet();
|
||
|
||
// Re-check nodeid's
|
||
var elements = document.getElementsByClassName("DeviceCheckbox"), checkcount = 0;
|
||
for (var i=0;i<elements.length;i++) { elements[i].checked = (checkedNodeids.indexOf(elements[i].value) >= 0); }
|
||
|
||
for (var i in deviceHeaders) { QH(i, deviceHeaders[i]); }
|
||
for (var i in deviceHeadersTitles) { Q(i).title = deviceHeadersTitles[i]; }
|
||
p1updateInfo();
|
||
|
||
// Take care of KVM surfaces in desktop view mode
|
||
if (view == 3) {
|
||
// Figure out and adjust the size to fill the width of the div
|
||
var vsize = [{ x: 180, y: 101 }, { x: 302, y: 169 }, { x: 454, y: 255 }][Q('sizeselect').selectedIndex];
|
||
//var realw = vsize.x + 2, tw = Q('xdevices').clientWidth - 30, xw = Math.floor(tw / realw);
|
||
var realw = vsize.x + 2, tw = totalDeviceViewWidth - 5, xw = Math.floor(tw / realw);
|
||
xw = realw + Math.floor((tw - (xw * realw)) / xw);
|
||
vsize.y = vsize.y * (xw / vsize.x);
|
||
vsize.x = xw;
|
||
|
||
for (var i in multiDesktop) { multiDesktop[i].xxdelete = true; }
|
||
for (var i in kvmDivs) {
|
||
var id = kvmDivs[i], shortid = id.split('/')[2], desk = multiDesktop[id];
|
||
if (desk != null) {
|
||
// This device already has a canvas, use it.
|
||
desk.m.CanvasId.setAttribute('style', 'background-color:black;width:' + vsize.x + 'px;height:' + vsize.y + 'px');
|
||
Q('xkvmid_' + shortid).appendChild(desk.m.CanvasId);
|
||
delete desk.xxdelete;
|
||
QH('skvmid_' + shortid, ['Disconnected', 'Connecting...', 'Setup...', '', ''][((desk.m.State == null)?desk.m.state:desk.m.State)]);
|
||
} else {
|
||
var node = getNodeFromId(id);
|
||
if ((desktopNode == node) && (desktop != null)) { // Check if the main desktop is this device, if it is, use that.
|
||
// This device already has a canvas, use it.
|
||
var c = desktop.m.CanvasId;
|
||
c.setAttribute('id', 'kvmid_' + shortid);
|
||
c.setAttribute('style', 'background-color:black;width:' + vsize.x + 'px;height:' + vsize.y + 'px');
|
||
c.setAttribute('onclick', 'toggleKvmDevice(\'' + id + '\')');
|
||
c.removeAttribute('onmousedown');
|
||
c.removeAttribute('onmouseup');
|
||
c.removeAttribute('onmousemove');
|
||
Q('xkvmid_' + shortid).appendChild(c);
|
||
QH('skvmid_' + shortid, ['Disconnected', 'Connecting...', 'Setup...', '', ''][((desktop.m.State == null)?desktop.m.state:desktop.m.State)]);
|
||
if (desktop.m.SendCompressionLevel) { desktop.m.SendCompressionLevel(1, multidesktopsettings.quality, multidesktopsettings.scaling, multidesktopsettings.framerate); }
|
||
desktop.shortid = shortid;
|
||
desktop.onStateChanged = onMultiDesktopStateChange;
|
||
multiDesktop[id] = desktop;
|
||
desktop = desktopNode = currentNode = null;
|
||
// Setup a replacement desktop
|
||
QH('DeskParent', '<canvas id="Desk" oncontextmenu="return false" onmousedown=dmousedown(event) onmouseup=dmouseup(event) onmousemove=dmousemove(event)></canvas>');
|
||
} else {
|
||
// This is a new device, create a canvas for it.
|
||
var c = document.createElement('canvas');
|
||
c.setAttribute('id', 'kvmid_' + shortid);
|
||
c.setAttribute('width', 640);
|
||
c.setAttribute('height', 480);
|
||
c.setAttribute('oncontextmenu', 'return false');
|
||
c.setAttribute('style', 'background-color:black;width:' + vsize.x + 'px;height:' + vsize.y + 'px');
|
||
c.setAttribute('onclick', 'toggleKvmDevice(\'' + id + '\')');
|
||
try { Q('xkvmid_' + shortid).appendChild(c); } catch (ex) {}
|
||
// Check if we need to auto-connect
|
||
if (Q('autoConnectDesktopCheckbox').checked == true) { setTimeout(function() { connectMultiDesktop(node, 1); }, 100); }
|
||
}
|
||
}
|
||
}
|
||
for (var i in multiDesktop) {
|
||
// If a device is no longer viewed, disconnect it.
|
||
if (multiDesktop[i].xxdelete == true) { multiDesktop[i].Stop(); delete multiDesktop[i]; }
|
||
else if (debugmode && multiDesktop[i].m && multiDesktop[i].m.onScreenSizeChange) {
|
||
mdeskAdjust(multiDesktop[i].m, multiDesktop[i].m.ScreenWidth, multiDesktop[i].m.ScreenHeight, multiDesktop[i].m.CanvasId); // Adjust screen size change
|
||
}
|
||
}
|
||
deskAdjust();
|
||
} else {
|
||
disconnectAllKvmFunction();
|
||
Q('autoConnectDesktopCheckbox').checked = false;
|
||
}
|
||
}
|
||
oldviewmode = view;
|
||
}
|
||
|
||
function toggleKvmDevice(nodeid) {
|
||
var node = getNodeFromId(nodeid), mesh = meshes[node.meshid], meshrights = mesh.links[userinfo._id].rights;
|
||
if ((meshrights & 8) || (meshrights & 256)) { // Requires remote control rights or desktop view only rights
|
||
//var conn = 0;
|
||
//if ((node.conn & 1) != 0) { conn = 1; } else if ((node.conn & 6) != 0) { conn = 2; } // Check what type of connect we can do (Agent vs AMT)
|
||
if (node.conn & 1) { connectMultiDesktop(node, 1); }
|
||
}
|
||
}
|
||
|
||
function getUserShortStr(node) {
|
||
if (node == null || node.users == null || node.users.length == 0) return '';
|
||
if (node.users.length > 1) { return '<span title="' + EscapeHtml(node.users.join(', ')) + '">' + node.users.length + ' users</span>'; }
|
||
var u = node.users[0], su = u, i = u.indexOf('\\');
|
||
if (i > 0) { su = u.substring(i + 1); }
|
||
su = EscapeHtml(su);
|
||
if (su.length > 15) { su = su.substring(0, 14) + '…'; }
|
||
return '<span title="' + EscapeHtml(u) + '">' + su + '</span>';
|
||
}
|
||
|
||
function autoConnectDesktops() { if (Q('autoConnectDesktopCheckbox').checked == true) { connectAllKvmFunction(); } }
|
||
function connectAllKvmFunction() { for (var i in nodes) { if (multiDesktop[nodes[i]._id] == null) { toggleKvmDevice(nodes[i]._id); } } }
|
||
function disconnectAllKvmFunction() { for (var nodeid in multiDesktop) { multiDesktop[nodeid].Stop(); } multiDesktop = {}; }
|
||
function onMultiDesktopStateChange(desk, state) { try { QH('skvmid_' + desk.shortid, ['Disconnected', 'Connecting...', 'Setup...', '', ''][state]); } catch (ex) {} }
|
||
|
||
function showMultiDesktopSettings() {
|
||
QV('d7amtkvm', false);
|
||
QV('d7meshkvm', true);
|
||
d7bitmapquality.value = multidesktopsettings.quality;
|
||
d7bitmapscaling.value = multidesktopsettings.scaling;
|
||
if (multidesktopsettings.framerate) { d7framelimiter.value = multidesktopsettings.framerate; } else { d7framelimiter.value = 1000; }
|
||
setDialogMode(7, "Remote Desktop Settings", 3, showMultiDesktopSettingsChanged);
|
||
}
|
||
|
||
function showMultiDesktopSettingsChanged() {
|
||
multidesktopsettings.quality = d7bitmapquality.value;
|
||
multidesktopsettings.scaling = d7bitmapscaling.value;
|
||
multidesktopsettings.framerate = d7framelimiter.value;
|
||
localStorage.setItem('multidesktopsettings', JSON.stringify(multidesktopsettings));
|
||
// Make changes to all current connections
|
||
for (var i in multiDesktop) { multiDesktop[i].m.SendCompressionLevel(1, multidesktopsettings.quality, multidesktopsettings.scaling, multidesktopsettings.framerate); }
|
||
}
|
||
|
||
function connectMultiDesktop(node, contype) {
|
||
var nodeid = node._id, shortid = nodeid.split('/')[2];
|
||
var desk = multiDesktop[nodeid];
|
||
if (desk == null) {
|
||
if (Q('kvmid_' + shortid) == null) return; // Check if this device is being displayed, if not, exit now.
|
||
if (contype == 2) {
|
||
// Setup the Intel AMT remote desktop
|
||
if ((node.intelamt.user == null) || (node.intelamt.user == '')) { return; }
|
||
desk = CreateAmtRedirect(CreateAmtRemoteDesktop('kvmid_' + shortid), authCookie);
|
||
desk.shortid = shortid;
|
||
//desk.debugmode = debugmode;
|
||
desk.onStateChanged = onMultiDesktopStateChange;
|
||
desk.m.bpp = 1;
|
||
desk.m.useZRLE = true;
|
||
desk.m.showmouse = true;
|
||
desk.m.onKvmData = function (data) { console.log('KVM Data received in multi-desktop mode, this is not supported.'); }; // KVM Data Channel not supported in multi-desktop right now.
|
||
//desk.m.onScreenSizeChange = deskAdjust;
|
||
if (debugmode > 0) { desk.m.onScreenSizeChange = mdeskAdjust; } // Multi-Desktop Adjust
|
||
desk.Start(nodeid, 16994, '*', '*', 0);
|
||
desk.contype = 2;
|
||
multiDesktop[nodeid] = desk;
|
||
} else if (contype == 1) {
|
||
// Setup the Mesh Agent remote desktop
|
||
desk = CreateAgentRedirect(meshserver, CreateAgentRemoteDesktop('kvmid_' + shortid), serverPublicNamePort, authCookie, domainUrl);
|
||
desk.shortid = shortid;
|
||
desk.attemptWebRTC = attemptWebRTC;
|
||
desk.onStateChanged = onMultiDesktopStateChange;
|
||
//desk.onConsoleMessageChange = function () { console.log('CONSOLEMSG:', desk.consoleMessage); }
|
||
desk.m.CompressionLevel = multidesktopsettings.quality;
|
||
desk.m.ScalingLevel = multidesktopsettings.scaling;
|
||
desk.m.FrameRateTimer = multidesktopsettings.framerate;
|
||
//desk.m.onDisplayinfo = deskDisplayInfo;
|
||
//desk.m.onScreenSizeChange = deskAdjust;
|
||
if (debugmode > 0) { desk.m.onScreenSizeChange = mdeskAdjust; } // Multi-Desktop Adjust
|
||
desk.Start(nodeid);
|
||
desk.contype = 1;
|
||
multiDesktop[nodeid] = desk;
|
||
}
|
||
} else {
|
||
// Disconnect and clean up the remote desktop
|
||
desk.Stop();
|
||
delete multiDesktop[nodeid];
|
||
}
|
||
}
|
||
|
||
function getMeshActions(mesh, meshrights) {
|
||
if ((meshrights & 4) == 0) return '';
|
||
var r = '';
|
||
if ((features & 1024) == 0) { // If CIRA is allowed
|
||
r += ' <a style=cursor:pointer;font-size:10px title="Add a new Intel® AMT computer that is located on the internet." onclick=addCiraDeviceToMesh(\"' + mesh._id + '\")>Add CIRA</a>';
|
||
}
|
||
if (mesh.mtype == 1) {
|
||
if ((features & 1) == 0) { // If not WAN-Only
|
||
r += ' <a style=cursor:pointer;font-size:10px title="Add a new Intel® AMT computer that is located on the local network." onclick=addDeviceToMesh(\"' + mesh._id + '\")>Add Local</a>';
|
||
r += ' <a style=cursor:pointer;font-size:10px title="Add a new Intel® AMT computer by scanning the local network." onclick=addAmtScanToMesh(\"' + mesh._id + '\")>Scan Network</a>';
|
||
}
|
||
}
|
||
if (mesh.mtype == 2) {
|
||
r += ' <a style=cursor:pointer;font-size:10px title="Add a new computer to this mesh by installing the mesh agent." onclick=addAgentToMesh(\"' + mesh._id + '\")>Add Agent</a>';
|
||
r += ' <a style=cursor:pointer;font-size:10px title="Invite someone to install the mesh agent on this mesh." onclick=inviteAgentToMesh(\"' + mesh._id + '\")>Invite</a>';
|
||
}
|
||
return r;
|
||
}
|
||
|
||
function addDeviceToMesh(meshid) {
|
||
if (xxdialogMode) return;
|
||
var mesh = meshes[meshid];
|
||
var x = "Add a new Intel® AMT device to device group \"" + EscapeHtml(mesh.name) + "\".<br /><br />";
|
||
x += addHtmlValue('Device Name', '<input id=dp1devicename style=width:230px maxlength=32 autocomplete=off onchange=validateDeviceToMesh() onkeyup=validateDeviceToMesh() />');
|
||
x += addHtmlValue('Hostname', '<input id=dp1hostname style=width:230px maxlength=32 autocomplete=off placeholder="Same as device name" onchange=validateDeviceToMesh() onkeyup=validateDeviceToMesh() />');
|
||
x += addHtmlValue('Username', '<input id=dp1username style=width:230px maxlength=32 autocomplete=off placeholder="admin" onchange=validateDeviceToMesh() onkeyup=validateDeviceToMesh() />');
|
||
x += addHtmlValue('Password', '<input id=dp1password type=password style=width:230px autocomplete=off maxlength=32 onchange=validateDeviceToMesh() onkeyup=validateDeviceToMesh() />');
|
||
x += addHtmlValue('Security', '<select id=dp1tls style=width:236px><option value=0>No TLS security</option><option value=1>TLS security required</option></select>');
|
||
setDialogMode(2, "Add Intel® AMT device", 3, addDeviceToMeshEx, x, meshid);
|
||
validateDeviceToMesh();
|
||
Q('dp1devicename').focus();
|
||
}
|
||
|
||
// Display the Intel AMT scanning dialog box
|
||
function addAmtScanToMesh(meshid) {
|
||
if (xxdialogMode) return;
|
||
var x = "Enter a range of IP addresses to scan for Intel AMT devices.<br /><br />";
|
||
x += addHtmlValue('IP Range', '<input id=dp1range style=width:184px value="192.168.1.0/24" onkeyup=addAmtScanToMeshKeyUp(event) /><input id=dp1rangebutton type=button value=Scan onclick=addAmtScanToMeshButton()></input>');
|
||
x += '<div id=dp1results style="width:100%;height:200px;background-color:white;border:1px gray solid;overflow-y:scroll"></div>';
|
||
setDialogMode(2, "Scan for Intel® AMT devices", 3, addAmtScanToMeshEx, x, meshid);
|
||
QE('idx_dlgOkButton', false);
|
||
QH('dp1results', '<div style=width:100%;text-align:center;margin-top:12px;color:gray;line-height:1.5>Sample IP range values<br />192.168.0.100<br />192.168.1.0/24<br />192.167.0.1-192.168.0.100</div>');
|
||
focusTextBox('dp1range');
|
||
}
|
||
|
||
function addAmtScanToMeshKeyUp(e) {
|
||
if (e.keyCode == 13) { haltEvent(e); addAmtScanToMeshButton(); }
|
||
}
|
||
|
||
// Called when OK is pressed on the Intel AMT scanning box
|
||
function addAmtScanToMeshEx(button, meshid) {
|
||
var elements = document.getElementsByClassName("DevScanCheckbox"), checkcount = 0;
|
||
for (var i=0;i<elements.length;i++) {
|
||
if (elements[i].checked) {
|
||
var ipaddr = elements[i].getAttribute('tag');
|
||
var amtinfo = amtScanResults[ipaddr];
|
||
meshserver.send({ action: 'addamtdevice', meshid: meshid, devicename: ipaddr, hostname: amtinfo.hostname, amtusername: '', amtpassword: '', amttls: amtinfo.tls });
|
||
}
|
||
}
|
||
}
|
||
|
||
// If the user presses the "Scan" button on the Intel AMT scanning dialog box, start a scan.
|
||
function addAmtScanToMeshButton() {
|
||
QE('dp1range', false);
|
||
QE('dp1rangebutton', false);
|
||
QH('dp1results', '<div style=width:100%;text-align:center;margin-top:12px>Scanning...</div>');
|
||
meshserver.send({ action: 'scanamtdevice', range: Q('dp1range').value });
|
||
}
|
||
|
||
// Called when a scanned computer is checked or unchecked.
|
||
function addAmtScanToMeshCheckbox() {
|
||
var elements = document.getElementsByClassName("DevScanCheckbox"), checkcount = 0;
|
||
for (var i=0;i<elements.length;i++) { if (elements[i].checked) checkcount++; }
|
||
QE('idx_dlgOkButton', checkcount > 0);
|
||
}
|
||
|
||
function addCiraDeviceToMesh(meshid) {
|
||
if (xxdialogMode) return;
|
||
var mesh = meshes[meshid];
|
||
|
||
// Replace non alphabetic characters (@ and $) with 'X' because MPS username cannot accept it.
|
||
var meshidx = meshid.split('/')[2].replace(/\@/g, 'X').replace(/\$/g, 'X');
|
||
|
||
var y = '<select id=dlgAddCiraSel onclick=dlgAddCiraSelClick() style=width:230px><option value=0>MeshCommander Script</option><option value=1>Manual Username/Password</option>';
|
||
if ((features & 16) == 0) { y += '<option value=2>Manual Certificate</option></select>'; } // Only display this option if Intel AMT CIRA with Mutual-Auth is allowed.
|
||
|
||
var x = '';
|
||
x += addHtmlValue('Setup Method', y);
|
||
x += '<hr>';
|
||
|
||
// Setup CIRA using a MeshCommander script (Pretty Simple)
|
||
x += "<div id=dlgAddCira0>To add a new Intel® AMT device to device group \"" + EscapeHtml(mesh.name) + "\" with CIRA, download the following script files and use <a href='http://meshcommander.com' rel='noreferrer noopener' target='_blank'>MeshCommander</a> to run the script to configure computers.<br /><br />";
|
||
x += addHtmlValue('Setup CIRA', '<a href="mescript.ashx?type=1&meshid=' + meshidx.substring(0, 16) + '" download>cira_setup.mescript</a>');
|
||
x += addHtmlValue('Cleanup CIRA', '<a href="mescript.ashx?type=2" download>cira_clean.mescript</a>');
|
||
x += "</div>";
|
||
|
||
// Setup CIRA with user/pass authentication (Somewhat difficult)
|
||
x += "<div id=dlgAddCira1 style=display:none>To add a new Intel® AMT device to device group \"" + EscapeHtml(mesh.name) + "\" with CIRA, load the following certificate as trusted root within Intel AMT";
|
||
if (serverinfo.mpspass) { x += " and authenticate to the server using this username and password.<br /><br />"; } else { x += " and authenticate to the server using this username and any password.<br /><br />"; }
|
||
x += addHtmlValue('Root Certificate', '<a href="MeshServerRootCert.cer" download>Root Certificate File</a>');
|
||
x += addHtmlValue('Username', '<input style=width:230px readonly value="' + meshidx.substring(0, 16) + '" />');
|
||
if (serverinfo.mpspass) { x += addHtmlValue('Password', '<input style=width:230px readonly value="' + EscapeHtml(serverinfo.mpspass) + '" />'); }
|
||
if (serverinfo != null) { x += addHtmlValue('MPS Server', '<input style=width:230px readonly value="' + EscapeHtml(serverinfo.mpsname) + ':' + serverinfo.mpsport + '" />'); }
|
||
x += "</div>";
|
||
|
||
// Setup CIRA with certificate authentication (Really difficult, only if TLS offload is not used)
|
||
if ((features & 16) == 0) {
|
||
x += "<div id=dlgAddCira2 style=display:none>To add a new Intel® AMT device to device group \"" + EscapeHtml(mesh.name) + "\" with CIRA, load the following certificate as trusted root within Intel AMT, authenticate using a client certificate with the following common name and connect to the following server.<br /><br />";
|
||
x += addHtmlValue('Root Certificate', '<a href="MeshServerRootCert.cer" download>Root Certificate File</a>');
|
||
x += addHtmlValue('Organization', '<input style=width:230px readonly value="' + meshidx + '" />');
|
||
if (serverinfo != null) { x += addHtmlValue('MPS Server', '<input style=width:230px readonly value="' + EscapeHtml(serverinfo.mpsname) + ':' + serverinfo.mpsport + '" />'); }
|
||
x += "</div>";
|
||
}
|
||
|
||
setDialogMode(2, "Add Intel® AMT CIRA device", 2, null, x, 'fileDownload');
|
||
}
|
||
|
||
function dlgAddCiraSelClick() {
|
||
var val = Q('dlgAddCiraSel').value;
|
||
QV('dlgAddCira0', val == 0);
|
||
QV('dlgAddCira1', val == 1);
|
||
QV('dlgAddCira2', val == 2);
|
||
}
|
||
|
||
// Return true is the input string looks like an email address
|
||
function checkEmail(str) {
|
||
var x = str.split('@');
|
||
var ok = ((x.length == 2) && (x[0].length > 0) && (x[1].split('.').length > 1) && (x[1].length > 2));
|
||
if (ok == true) { var y = x[1].split('.'); for (var i in y) { if (y[i].length == 0) { ok = false; } } }
|
||
return ok;
|
||
}
|
||
|
||
function inviteAgentToMesh(meshid) {
|
||
if (xxdialogMode) return;
|
||
var x = '', mesh = meshes[meshid];
|
||
if (features & 64) {
|
||
x += addHtmlValue('Invitation Type', '<select id=d2InviteType onchange=d2ChangedInviteType() style=width:236px><option value=0>Link invitation</option><option value=1>Email invitation</option></select>') + "<hr />";
|
||
x += "<div id=emailInviteDiv style=display:none>Invite someone to install the mesh agent. An email with be sent with the link to the mesh agent installation for the \"" + EscapeHtml(mesh.name) + "\" device group.<br /><br />";
|
||
x += addHtmlValue('Name (optional)', '<input id=agentInviteName value="" style=width:230px maxlength=64 />');
|
||
x += addHtmlValue('Email', '<input id=agentInviteEmail style=width:230px placeholder="example@email.com" onkeyup=validateAgentInvite()></input>');
|
||
x += addHtmlValue('Operating System', '<select id=agentInviteNameOs onchange=d2ChangedInviteType() style=width:236px><option value=4>Send installation link</option><option value=0 selected>Any supported</option><option value=1>Windows only</option><option value=3>Apple MacOS only</option><option value=2>Linux only</option></select>');
|
||
x += '<div id=d2agentexpirediv>';
|
||
x += addHtmlValue('Link Expiration', '<select id=agentInviteExpire style=width:236px><option value=1>1 hour</option><option value=8>8 hours</option><option value=24>1 day</option><option value=168>1 week</option><option value=5040>1 month</option><option value=0>Unlimited</option></select>');
|
||
x += '</div>';
|
||
x += addHtmlValue('Installation Type', '<select id=agentInviteType style=width:236px><option value=0>Background and interactive</option><option value=2>Background only</option><option value=1>Interactive only</option></select>');
|
||
x += addHtmlValue('Message<br />(optional)', '<textarea id=agentInviteMessage value="" style=width:230px;height:100px;resize:none maxlength=1024 /></textarea>');
|
||
x += '</div>';
|
||
}
|
||
x += '<div id=urlInviteDiv>Invite someone to install the mesh agent by sharing an invitation link. This link points the user to installation instructions for the \"' + EscapeHtml(mesh.name) + '\" device group. The link is public and no account for this server is needed.<br /><br />';
|
||
x += addHtmlValue('Link Expiration', '<select id=d2inviteExpire style=width:236px onchange=d2RequestInvitationLink()><option value=1>1 hour</option><option value=8>8 hours</option><option value=24>1 day</option><option value=168>1 week</option><option value=5040>1 month</option><option value=0>Unlimited</option></select>');
|
||
x += '<div id=agentInvitationLinkDiv style="text-align:center;font-size:large;margin:16px;display:none"><a id=agentInvitationLink target="_blank" href="" style=cursor:pointer></a> <img src=images/link4.png height=10 width=10 title="Copy link to clipboard" style=cursor:pointer onclick=d2CopyInviteToClip()></div></div>';
|
||
setDialogMode(2, "Invite", 3, performAgentInvite, x, meshid);
|
||
if (features & 64) { d2ChangedInviteType(); } else { validateAgentInvite(); }
|
||
d2RequestInvitationLink();
|
||
}
|
||
|
||
function d2RequestInvitationLink() {
|
||
meshserver.send({ action: 'createInviteLink', meshid: xxdialogTag, expire: parseInt(Q('d2inviteExpire').value), flags: 0 });
|
||
}
|
||
|
||
function d2ChangedInviteType() {
|
||
QV('urlInviteDiv', Q('d2InviteType').value == 0);
|
||
QV('d2agentexpirediv', Q('agentInviteNameOs').value == 4);
|
||
QV('emailInviteDiv', Q('d2InviteType').value == 1);
|
||
validateAgentInvite();
|
||
}
|
||
|
||
function d2CopyInviteToClip() { copyTextToClip(Q('agentInvitationLink').href); }
|
||
|
||
function validateAgentInvite() {
|
||
if ((features & 64) && (Q('d2InviteType').value == 1)) {
|
||
QE('idx_dlgOkButton', checkEmail(Q('agentInviteEmail').value));
|
||
QV('idx_dlgCancelButton', true);
|
||
} else {
|
||
QE('idx_dlgOkButton', true);
|
||
QV('idx_dlgCancelButton', false);
|
||
}
|
||
}
|
||
|
||
function performAgentInvite(button, meshid) {
|
||
if ((features & 64) && (Q('d2InviteType').value == 1)) {
|
||
meshserver.send({ action: 'inviteAgent', meshid: meshid, email: Q('agentInviteEmail').value, name: Q('agentInviteName').value, os: Q('agentInviteNameOs').value, flags: Q('agentInviteType').value, msg: Q('agentInviteMessage').value, expire: parseInt(Q('agentInviteExpire').value) });
|
||
}
|
||
}
|
||
|
||
function addAgentToMesh(meshid) {
|
||
if (xxdialogMode) return;
|
||
var mesh = meshes[meshid], x = '', installType = 0;
|
||
x += addHtmlValue('Operating System', '<select id=aginsSelect onchange=addAgentToMeshClick() style=width:236px><option value=0>Windows</option><option value=1>Linux</option><option value=2>Apple MacOS</option><option value=3>Windows (UnInstall)</option><option value=4>Linux (UnInstall)</option></select>');
|
||
x += '<div id=aginsTypeDiv>';
|
||
x += addHtmlValue('Installation Type', '<select id=aginsType onchange=addAgentToMeshClick() style=width:236px><option value=0>Background & interactive</option><option value=2>Background only</option><option value=1>Interactive only</option></select>');
|
||
x += '</div><hr>';
|
||
|
||
// \/:*?"<>|
|
||
var meshfilename = mesh.name
|
||
meshfilename = meshfilename.split('\\').join('').split('/').join('').split(':').join('').split('*').join('').split('?').join('').split('"').join('').split('<').join('').split('>').join('').split('|').join('').split(' ').join('').split('\'').join('');
|
||
|
||
// Windows agent install
|
||
//x += "<div id=agins_windows>To add a new computer to device group \"" + EscapeHtml(mesh.name) + "\", download the mesh agent and configuration file and install the agent on the computer to manage.<br /><br />";
|
||
x += "<div id=agins_windows>To add a new computer to device group \"" + EscapeHtml(mesh.name) + "\", download the mesh agent and install it the computer to manage. This agent has server and device group information embedded within it.<br /><br />";
|
||
x += addHtmlValue('Mesh Agent', '<a id=aginsw32lnk href="meshagents?id=3&meshid=' + meshid.split('/')[2] + '&installflags=0" download onclick="setDialogMode(0)" title="32bit version of the MeshAgent">Windows (.exe)</a>');
|
||
x += addHtmlValue('Mesh Agent', '<a id=aginsw64lnk href="meshagents?id=4&meshid=' + meshid.split('/')[2] + '&installflags=0" download onclick="setDialogMode(0)" title="64bit version of the MeshAgent">Windows x64 (.exe)</a>');
|
||
if (debugmode > 0) { x += addHtmlValue('Settings File', '<a id=aginswmshlnk href="meshsettings?id=' + meshid.split('/')[2] + '&installflags=0" rel="noreferrer noopener" target="_blank">' + EscapeHtml(mesh.name) + ' settings (.msh)</a>'); }
|
||
x += "</div>";
|
||
|
||
// Linux agent install
|
||
x += "<div id=agins_linux style=display:none>To add a computer to " + EscapeHtml(mesh.name) + " run the following command. Root credentials will be needed.<br />";
|
||
x += '<textarea id=agins_linux_area rows=2 cols=20 readonly=readonly style=width:100%;resize:none;height:120px;overflow:scroll;font-size:12px readonly></textarea>';
|
||
x += "</div>";
|
||
|
||
// MacOS agent install
|
||
x += "<div id=agins_osx style=display:none>To add a new computer to device group \"" + EscapeHtml(mesh.name) + "\", download the mesh agent and install it the computer to manage. This agent installer has server and device group information embedded within it.<br /><br />";
|
||
x += addHtmlValue('Mesh Agent', '<a href="meshosxagent?id=16&meshid=' + meshid.split('/')[2] + '" rel="noreferrer noopener" target="_blank" title="64bit version of MacOS Mesh Agent">MacOS Agent (64bit)</a> <img src=images/link4.png height=10 width=10 title="Copy MacOS agent URL to clipboard" style=cursor:pointer onclick=copyAgentUrl("meshosxagent?id=16&meshid=' + meshid.split('/')[2] + '",0)>');
|
||
x += "</div>";
|
||
|
||
// Windows agent uninstall
|
||
x += "<div id=agins_windows_un style=display:none>To remove a mesh agent, download the file below, run it and click \"uninstall\".<br /><br />";
|
||
x += addHtmlValue('Mesh Agent', '<a href="meshagents?id=3" download onclick="setDialogMode(0)" title="32bit version of the MeshAgent">Windows (.exe)</a>');
|
||
x += addHtmlValue('Mesh Agent', '<a href="meshagents?id=4" download onclick="setDialogMode(0)" title="64bit version of the MeshAgent">Windows x64 (.exe)</a>');
|
||
x += "</div>";
|
||
|
||
// Linux agent uninstall
|
||
x += "<div id=agins_linux_un style=display:none>To remove a mesh agent, run the following command. Root credentials will be needed.<br />";
|
||
x += '<textarea id=agins_linux_area_un rows=2 cols=20 readonly=readonly style=width:100%;resize:none;height:120px;overflow:scroll;font-size:12px readonly></textarea>';
|
||
x += "</div>";
|
||
|
||
setDialogMode(2, "Add Mesh Agent", 2, null, x, 'fileDownload');
|
||
var servername = serverinfo.name;
|
||
if ((servername.indexOf('.') == -1) || ((features & 2) != 0)) { servername = window.location.hostname; } // If the server name is not set or it's in LAN-only mode, use the URL hostname as server name.
|
||
var domainUrlNoSlash = domainUrl.substring(0, domainUrl.length - 1);
|
||
|
||
if (serverinfo.https == true)
|
||
{
|
||
var portStr = (serverinfo.port == 443)?'':(":" + serverinfo.port);
|
||
if ((features & 0x2000) == 0)
|
||
{
|
||
Q('agins_linux_area').value = "(wget https://" + servername + portStr + domainUrl + "meshagents?script=1 --no-check-certificate -O ./meshinstall.sh || wget https://" + servername + portStr + domainUrl + "meshagents?script=1 --no-proxy --no-check-certificate -O ./meshinstall.sh) && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh https://" + servername + portStr + domainUrlNoSlash + " '" + meshid.split('/')[2] + "'\r\n";
|
||
Q('agins_linux_area_un').value = "(wget https://" + servername + portStr + domainUrl + "meshagents?script=1 --no-check-certificate -O ./meshinstall.sh || wget https://" + servername + portStr + domainUrl + "meshagents?script=1 --no-proxy --no-check-certificate -O ./meshinstall.sh) && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh uninstall\r\n";
|
||
}
|
||
else
|
||
{
|
||
// Server asked that agent be installed to preferably not use a HTTP proxy.
|
||
Q('agins_linux_area').value = "wget https://" + servername + portStr + domainUrl + "meshagents?script=1 --no-proxy --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh https://" + servername + portStr + domainUrlNoSlash + " '" + meshid.split('/')[2] + "'\r\n";
|
||
Q('agins_linux_area_un').value = "wget https://" + servername + portStr + domainUrl + "meshagents?script=1 --no-proxy --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh uninstall\r\n";
|
||
}
|
||
}
|
||
else
|
||
{
|
||
var portStr = (serverinfo.port == 80) ? '' : (":" + serverinfo.port);
|
||
if ((features & 0x2000) == 0)
|
||
{
|
||
Q('agins_linux_area').value = "(wget http://" + servername + portStr + domainUrl + "meshagents?script=1 -O ./meshinstall.sh || wget http://" + servername + portStr + domainUrl + "meshagents?script=1 --no-proxy -O ./meshinstall.sh) && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh http://" + servername + portStr + domainUrlNoSlash + " '" + meshid.split('/')[2] + "'\r\n";
|
||
Q('agins_linux_area_un').value = "(wget http://" + servername + portStr + domainUrl + "meshagents?script=1 -O ./meshinstall.sh || wget http://" + servername + portStr + domainUrl + "meshagents?script=1 --no-proxy -O ./meshinstall.sh) && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh uninstall\r\n";
|
||
}
|
||
else
|
||
{
|
||
// Server asked that agent be installed to preferably not use a HTTP proxy.
|
||
Q('agins_linux_area').value = "wget http://" + servername + portStr + domainUrl + "meshagents?script=1 --no-proxy -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh http://" + servername + portStr + domainUrlNoSlash + " '" + meshid.split('/')[2] + "'\r\n";
|
||
Q('agins_linux_area_un').value = "wget http://" + servername + portStr + domainUrl + "meshagents?script=1 --no-proxy -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh uninstall\r\n";
|
||
}
|
||
}
|
||
Q('aginsSelect').focus();
|
||
addAgentToMeshClick();
|
||
}
|
||
|
||
function copyAgentUrl(url,addflag) {
|
||
var servername = serverinfo.name;
|
||
if ((servername.indexOf('.') == -1) || ((features & 2) != 0)) { servername = window.location.hostname; } // If the server name is not set or it's in LAN-only mode, use the URL hostname as server name.
|
||
var domainUrlNoSlash = domainUrl.substring(0, domainUrl.length - 1);
|
||
var portStr = (serverinfo.port == 443) ? '' : (":" + serverinfo.port);
|
||
var c = "https://" + servername + portStr + domainUrl + url;
|
||
if (addflag == 1) c += Q('aginsType').value;
|
||
copyTextToClip(c);
|
||
}
|
||
|
||
function addAgentToMeshClick() {
|
||
var v = Q('aginsSelect').value;
|
||
QV('agins_windows', v == 0);
|
||
QV('agins_linux', v == 1);
|
||
QV('agins_osx', v == 2);
|
||
QV('agins_windows_un', v == 3);
|
||
QV('agins_linux_un', v == 4);
|
||
QV('aginsTypeDiv', v == 0);
|
||
|
||
// Fix the links if needed
|
||
//Q('aginsw32lnk').href = (Q('aginsw32lnk').href.split('installflags=')[0]) + 'installflags=' + Q('aginsType').value;
|
||
//Q('aginsw64lnk').href = (Q('aginsw64lnk').href.split('installflags=')[0]) + 'installflags=' + Q('aginsType').value;
|
||
if (debugmode > 0) { Q('aginswmshlnk').href = (Q('aginswmshlnk').href.split('installflags=')[0]) + 'installflags=' + Q('aginsType').value; }
|
||
}
|
||
|
||
function validateDeviceToMesh() {
|
||
QE('idx_dlgOkButton', (Q('dp1devicename').value.length > 0) && (passwordcheck(Q('dp1password').value)));
|
||
}
|
||
|
||
function addDeviceToMeshEx(button, meshid) {
|
||
var amtuser = Q('dp1username').value;
|
||
if (amtuser == '') amtuser = 'admin';
|
||
var host = Q('dp1hostname').value;
|
||
if (host == '') host = Q('dp1devicename').value;
|
||
meshserver.send({ action: 'addamtdevice', meshid: meshid, devicename: Q('dp1devicename').value, hostname: host, amtusername: amtuser, amtpassword: Q('dp1password').value, amttls: Q('dp1tls').value });
|
||
}
|
||
|
||
function deviceHeaderSet() {
|
||
if (deviceHeaderId == 0) { deviceHeaderId = 1; return; }
|
||
deviceHeaders["DevxHeader" + deviceHeaderId] = deviceHeaderTotal + ((deviceHeaderTotal == 1) ? ' node' : ' nodes');
|
||
//var title = '';
|
||
//for (x in deviceHeaderCount) { if (title.length > 0) title += ', '; title += deviceHeaderCount[x] + ' ' + PowerStateStr2(x); }
|
||
//deviceHeadersTitles["DevxHeader" + deviceHeaderId] = title;
|
||
deviceHeaderId++;
|
||
deviceHeaderCount = {};
|
||
deviceHeaderTotal = 0;
|
||
}
|
||
|
||
var powerStateStrings = ['', '<span title="Device is powered on.">Powered</span>', '<span title="Device is in sleep state (S1).">Sleeping</span>', '<span title="Device is in sleep state (S2).">Sleeping</span>', '<span title="Device is in deep sleep state (S3).">Deep Sleep</span>', '<span title="Device is in hibernating state (S4).">Hibernating</span>', '<span title="Device is in powered off state (S5).">Soft-Off</span>', '<span title="Device is detected but power state could not be obtained.">Present</span>'];
|
||
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 = ['pwsTransparent', 'pwsBlack', 'pwsBlue', 'pwsBlue2', 'pwsLightblue', 'pwsBlueviolet', 'pwsDarkgreen', 'pwsLightseagreen', 'pwsLightseagreen2'];
|
||
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 title="Mesh agent is connected and ready for use.">Agent</span>'); }
|
||
if ((node.conn & 2) != 0) { states.push('<span title="Intel® AMT CIRA is connected and ready for use.">CIRA</span>'); }
|
||
else if ((node.conn & 4) != 0) { states.push('<span title="Intel® AMT is routable.">Intel® AMT</span>'); }
|
||
if ((node.conn & 8) != 0) { states.push('<span title="Mesh agent is reachable using another agent as relay.">Relay</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 selectallButtonFunction() {
|
||
var elements = document.getElementsByClassName("DeviceCheckbox"), checkcount = 0;
|
||
for (var i=0;i<elements.length;i++) { if (elements[i].checked === true) checkcount++; }
|
||
for (var i=0;i<elements.length;i++) { elements[i].checked = (checkcount == 0); }
|
||
p1updateInfo();
|
||
}
|
||
|
||
function p1updateInfo() {
|
||
var elements = document.getElementsByClassName("DeviceCheckbox"), checkcount = 0;
|
||
for (var i=0;i<elements.length;i++) { if (elements[i].checked === true) { checkcount++; } }
|
||
if (checkcount > 0) {
|
||
QE('GroupActionButton', true);
|
||
Q('SelectAllButton').value = 'Select None';
|
||
QV('cxmgroupsplit', true);
|
||
QV('cxmdesktop', true);
|
||
} else {
|
||
QE('GroupActionButton', false);
|
||
Q('SelectAllButton').value = 'Select All';
|
||
QV('cxmgroupsplit', false);
|
||
QV('cxmdesktop', false);
|
||
}
|
||
}
|
||
|
||
function groupActionFunction() {
|
||
var x = "Select an operation to perform on all selected devices. Actions will be performed only with proper rights.<br /><br />";
|
||
x += addHtmlValue('Operation', '<select id=d2groupop><option value=100>Wake-up devices</option><option value=4>Sleep devices</option><option value=3>Reset devices</option><option value=2>Power off devices</option><option value=102>Move to device group</option><option value=101>Delete devices</option></select>');
|
||
setDialogMode(2, "Group Action", 3, groupActionFunctionEx, x);
|
||
}
|
||
|
||
// Get the list of checked devices, removes any duplicates.
|
||
function getCheckedDevices() {
|
||
var nodeids = [], elements = document.getElementsByClassName("DeviceCheckbox"), checkcount = 0;
|
||
for (var i=0;i<elements.length;i++) { if (elements[i].checked) { if (elements[i].value) { var nid = elements[i].value.substring(6); if (nodeids.indexOf(nid) == -1) { nodeids.push(nid); } } } }
|
||
return nodeids;
|
||
}
|
||
|
||
function groupActionFunctionEx() {
|
||
var op = Q('d2groupop').value;
|
||
if (op == 100) {
|
||
// Group wake
|
||
meshserver.send({ action: 'wakedevices', nodeids: getCheckedDevices() });
|
||
} else if (op == 101) {
|
||
// Group delete, ask for confirmation
|
||
var x = "Confirm delete selected devices(s)?<br /><br />";
|
||
x += "<label><input id=d2check type=checkbox onchange=d2groupActionFunctionDelEx() />Confirm</label>";
|
||
setDialogMode(2, "Delete Nodes", 3, groupActionFunctionDelEx, x);
|
||
QE('idx_dlgOkButton', false);
|
||
} else if (op == 102) {
|
||
// Move computers to a different group
|
||
p10showChangeGroupDialog(getCheckedDevices());
|
||
} else {
|
||
// Power operation
|
||
meshserver.send({ action: 'poweraction', nodeids: getCheckedDevices(), actiontype: op });
|
||
}
|
||
}
|
||
|
||
function d2groupActionFunctionDelEx() { QE('idx_dlgOkButton', Q('d2check').checked); }
|
||
function groupActionFunctionDelEx() { meshserver.send({ action: 'removedevices', nodeids: getCheckedDevices() }); }
|
||
|
||
function onSortSelectChange(skipsave) {
|
||
sort = document.getElementById("sortselect").selectedIndex;
|
||
if (!skipsave) { putstore("sort", sort); }
|
||
}
|
||
|
||
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) return -1; if (ap < bp) return 1; 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; } } 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; }
|
||
function onSearchFocus(x) { searchFocus = x; }
|
||
function onMapSearchFocus(x) { mapSearchFocus = x; }
|
||
function onUserSearchFocus(x) { userSearchFocus = x; }
|
||
function onConsoleFocus(x) { consoleFocus = x; }
|
||
|
||
function onSearchInputChanged() {
|
||
var x = Q('SearchInput').value.toLowerCase().trim(); putstore("_search", x);
|
||
var userSearch = null, ipSearch = null, groupSearch = null;
|
||
if (x.startsWith('user:')) { userSearch = x.substring(5); }
|
||
else if (x.startsWith('u:')) { userSearch = x.substring(2); }
|
||
else if (x.startsWith('ip:')) { ipSearch = x.substring(3); }
|
||
else if (x.startsWith('group:')) { groupSearch = x.substring(6); }
|
||
else if (x.startsWith('g:')) { groupSearch = x.substring(2); }
|
||
|
||
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 (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 {
|
||
// 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; } }
|
||
}
|
||
}
|
||
|
||
var contextelement = null;
|
||
function handleContextMenu(event) {
|
||
hideContextMenu();
|
||
var scrollLeft = (window.pageXOffset !== null) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft;
|
||
var scrollTop = (window.pageYOffset !== null) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
|
||
var elem = document.elementFromPoint(event.pageX - scrollLeft, event.pageY - scrollTop);
|
||
if (elem && elem != null && elem.id == "MxMESH") {
|
||
contextelement = elem;
|
||
var contextmenudiv = document.getElementById("meshContextMenu");
|
||
contextmenudiv.style.left = event.pageX + "px";
|
||
contextmenudiv.style.top = event.pageY + "px";
|
||
contextmenudiv.style.display = "block";
|
||
} else {
|
||
while (elem && elem != null && elem.id != "devs") { elem = elem.parentElement; }
|
||
if (!elem || elem == null) return true;
|
||
contextelement = elem;
|
||
var contextmenudiv = document.getElementById("contextMenu");
|
||
contextmenudiv.style.left = event.pageX + "px";
|
||
contextmenudiv.style.top = event.pageY + "px";
|
||
contextmenudiv.style.display = "block";
|
||
}
|
||
|
||
// Get the node and set the menu options
|
||
var nodeid = contextelement.children[1].attributes.onclick.value;
|
||
var node = getNodeFromId(nodeid.substring(12, nodeid.length - 18));
|
||
var mesh = meshes[node.meshid];
|
||
var meshlinks = mesh.links[userinfo._id];
|
||
var meshrights = meshlinks.rights;
|
||
var consoleRights = ((meshrights & 16) != 0);
|
||
|
||
// Check if we have terminal and file access
|
||
var terminalAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 512) == 0));
|
||
var fileAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 1024) == 0));
|
||
|
||
QV('cxdesktop', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 1) != 0) || (node.intelamt && (node.intelamt.state == 2))) && ((meshrights & 8) || (meshrights & 256)));
|
||
QV('cxterminal', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 2) != 0) || (node.intelamt && (node.intelamt.state == 2))) && (meshrights & 8) && terminalAccess);
|
||
QV('cxfiles', ((mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 4) != 0))) && (meshrights & 8) && fileAccess);
|
||
QV('cxevents', (node.intelamt != null) && ((node.intelamt.state == 2) || (node.conn & 2)) && (meshrights & 8));
|
||
QV('cxconsole', (consoleRights && (mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 8) != 0))) && (meshrights & 8));
|
||
|
||
return haltEvent(event);
|
||
}
|
||
|
||
function cmaction(action,event) {
|
||
var nodeid = contextelement.children[1].attributes.onclick.value;
|
||
nodeid = nodeid.substring(12, nodeid.length - 18);
|
||
if (action == 7) { Q('viewselect').value = 3; Q('viewselect').onchange(); Q('autoConnectDesktopCheckbox').checked = true; Q('autoConnectDesktopCheckbox').onclick(); } // Multi-Desktop
|
||
if ((action > 0) && (action < 7)) {
|
||
var panel = [0, 10, 12, 11, 13, 16, 15][action]; // (invalid), General, Desktop, Terminal, Files, Events, Console
|
||
if (event && (event.shiftKey == true)) {
|
||
// Open the device in a different tab
|
||
window.open(window.location.origin + '?node=' + nodeid.split('/')[2] + '&viewmode=' + panel + '&hide=16', 'meshcentral:' + nodeid);
|
||
} else {
|
||
// Go to the right panel
|
||
gotoDevice(nodeid, panel);
|
||
|
||
// If possible, connect...
|
||
var mesh = meshes[currentNode.meshid];
|
||
if ((currentNode.conn & 1) && (mesh.mtype == 2)) {
|
||
if ((panel == 11) && (desktop == null) && (currentNode.agent.caps & 1)) { connectDesktop(null, 1); } // Desktop
|
||
if ((panel == 12) && (terminal == null) && (currentNode.agent.caps & 2)) { connectTerminal(null, 1); } // Terminal
|
||
if ((panel == 13) && (files == null)) { connectFiles(null); } // files
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
function cmmeshaction(action) {
|
||
var meshid = contextelement.attributes.onclick.value.substring(32, (32 + 69));
|
||
var elements = document.getElementsByClassName("DeviceCheckbox");
|
||
if (action == 1) { for (var i = 0; i < elements.length; i++) { if ( (elements[i].attributes) && (elements[i].attributes['class']['value'].substring(0, 69) == meshid)) { elements[i].checked = true; } } }
|
||
if (action == 2) { for (var i = 0; i < elements.length; i++) { if ( (elements[i].attributes) && (elements[i].attributes['class']['value'].substring(0, 69) == meshid)) { elements[i].checked = false; } } }
|
||
//if (action == 3) { window.location = "multidesktop.aspx?mesh=" + meshid + "&auto=1"; }
|
||
p1updateInfo();
|
||
}
|
||
|
||
function hideContextMenu() {
|
||
QV('contextMenu', false);
|
||
QV('meshContextMenu', false);
|
||
contextelement = null;
|
||
}
|
||
|
||
//
|
||
// DEVICES MAP
|
||
//
|
||
|
||
// Maps code starts from here. Initialize all the variables
|
||
var xxmap = {
|
||
map: null,
|
||
contextmenu: null,
|
||
activeInteractions: [], // Save Modified features in this list
|
||
showindex: 0,
|
||
markersSource: null, // Initialize a Source Vector
|
||
markersLayer: null,
|
||
mapLayer: null, // Create a tile and use OSM source
|
||
mapView: null, // Sets the initial view
|
||
}
|
||
|
||
// Add a feature for every Node and change style if connection status changes
|
||
function updateMapMarkers(selectedMesh) {
|
||
if ((xxmap != null) && (xxmap.map == null)) { try { loadmap(); } catch (ex) { console.error('loadmap() exception', ex); } }
|
||
if (xxmap == null) return;
|
||
var boundingBox = null;
|
||
for (var i in nodes) {
|
||
try {
|
||
var loc = map_parseNodeLoc(nodes[i]), feature = xxmap.markersSource.getFeatureById(nodes[i]._id);
|
||
if ((loc != null) && ((nodes[i].meshid == selectedMesh) || (selectedMesh == null))) { // Draw markers for devices with locations
|
||
var lat = loc[0], lon = loc[1], type = loc[2];
|
||
if (boundingBox == null) { boundingBox = [ lat, lon, lat, lon, 0 ]; } else { if (lat < boundingBox[0]) { boundingBox[0] = lat; } if (lon < boundingBox[1]) { boundingBox[1] = lon; } if (lat > boundingBox[2]) { boundingBox[2] = lat; } if (lon > boundingBox[3]) { boundingBox[3] = lon; } }
|
||
if (feature == null) { addFeature(nodes[i]); boundingBox[4] = 1; } else { updateFeature(nodes[i], feature); feature.setStyle(markerStyle(nodes[i], loc[2])); } // Update Feature
|
||
} else {
|
||
if (feature) { xxmap.markersSource.removeFeature(feature); }
|
||
}
|
||
} catch (ex) { console.error('updateMapMarkers() exception', ex, JSON.stringify(nodes[i])); }
|
||
}
|
||
return boundingBox;
|
||
}
|
||
|
||
// Show node details on hovering over a feature
|
||
var map_cm_popup = new ol.Overlay({ element: Q('xmap-info-window'), positioning: 'bottom-center', stopEvent: false });
|
||
|
||
// Edit Marker item
|
||
var map_cm_editMarker = { text: "Modify node location", callback: function (obj) { modifyMarkerloc(obj.data); } };
|
||
|
||
// Clear Marker item
|
||
var map_cm_clearMarker = { text: "Remove node location", callback: function (obj) {
|
||
meshserver.send({ action: 'changedevice', nodeid: obj.data.a, userloc: [] }); // Clear the user position marker
|
||
}};
|
||
|
||
// Save Marker item
|
||
var map_cm_saveMarker = { text: "Save node location", callback: function (obj) { saveMarkerloc(obj.data); } };
|
||
|
||
// Build a context menu for a feature
|
||
var map_cm_nodemenu_items = [
|
||
{ text: "General information", callback: function (obj) { if (obj.data !=null) { gotoDevice(obj.data, 10); } } },
|
||
{ text: "Desktop", callback: function (obj) { if (obj.data !=null) { gotoDevice(obj.data, 11); } } },
|
||
{ text: "Terminal", callback: function (obj) { if (obj.data !=null) { gotoDevice(obj.data, 12); } } },
|
||
{ text: "Intel® AMT", callback: function (obj) { if (obj.data !=null) { gotoDevice(obj.data, 14); } } },
|
||
'-',
|
||
{ text: 'Zoom-in to extent', callback: function(obj) { var coords = obj.data.getGeometry().getCoordinates(); zoomToLocation(coords, 19); } },
|
||
{ text: 'Zoom-out to extent', callback: function(obj) { var coords = obj.data.getGeometry().getCoordinates(); zoomToLocation(coords, 2); } }
|
||
];
|
||
|
||
// Context menu for clicks other than on feature
|
||
var contextmenu_items = [
|
||
{ text: 'Refresh', callback: function () { refreshMap(true, true); } },
|
||
{ text: 'Zoom to fit extent', callback: function () { zoomToFitExtent(); } },
|
||
{ text: 'Center map here', callback: function(obj) { xxmap.mapView.animate({ center: obj.coordinate } ); } },
|
||
{ text: 'Place node here', callback: function(obj) { placeNode(obj.coordinate); } }
|
||
];
|
||
|
||
function stringToIntHash(str) {
|
||
var hash = 0, i;
|
||
for (i = 0; i < str.length; i++) { hash = ((hash << 5) - hash) + str.charCodeAt(i); hash |= 0; }
|
||
return hash;
|
||
};
|
||
|
||
// Get the lat/lon from a node
|
||
function map_parseNodeLoc(node) {
|
||
var loc = null, t = 0;
|
||
if (node.iploc) { loc = node.iploc; t = 1; }
|
||
if (node.wifiloc) { loc = node.wifiloc; t = 2; }
|
||
if (node.gpsloc) { loc = node.gpsloc; t = 3; }
|
||
if (node.userloc) { loc = node.userloc; t = 4; }
|
||
if ((loc == null) || (typeof loc != 'string')) return null;
|
||
loc = loc.split(',');
|
||
if (t == 1) {
|
||
// If this is IP location, randomize the position a little.
|
||
return [ parseFloat(loc[0]) + (stringToIntHash(node._id.substring(0, 20)) / 100000000000), parseFloat(loc[1]) + (stringToIntHash(node._id.substring(20)) / 100000000000), t ];
|
||
} else {
|
||
// Return the real position
|
||
return [ parseFloat(loc[0]), parseFloat(loc[1]), t ];
|
||
}
|
||
}
|
||
|
||
// Load the entire map
|
||
function loadmap() {
|
||
if (xxmap == null) return;
|
||
if ((features & 0x8000) == 0) { QV('viewselectmapoption', false); QV('devViewButton4', false); xxmap = null; return; } // Geolocation not supported
|
||
try {
|
||
// Initialize a Source Vector
|
||
xxmap.markersSource = new ol.source.Vector();
|
||
|
||
xxmap.markersLayer = new ol.layer.Vector({
|
||
source: xxmap.markersSource
|
||
});
|
||
|
||
// Create a tile and use OSM source
|
||
xxmap.mapLayer = new ol.layer.Tile({ source: new ol.source.OSM() });
|
||
|
||
xxmap.mapView = new ol.View({ // Set the initial view
|
||
center: ol.proj.transform([0, 0], 'EPSG:4326', 'EPSG:3857'),
|
||
zoom: 2,
|
||
minZoom: 2,
|
||
maxZoom: 20,
|
||
extent: ol.proj.transformExtent([-100000, -69.55, 100000, 69.55], 'EPSG:4326', 'EPSG:3857')
|
||
});
|
||
|
||
xxmap.map = new ol.Map({
|
||
target: 'xdevicesmap',
|
||
layers: [xxmap.mapLayer, xxmap.markersLayer],
|
||
view: xxmap.mapView
|
||
});
|
||
|
||
xxmap.map.addOverlay(map_cm_popup);
|
||
|
||
// Goto information tab if a user clicks on a feature
|
||
xxmap.map.on('click', function(evt) {
|
||
var feature = xxmap.map.forEachFeatureAtPixel(evt.pixel, function(feat, layer) { return feat; });
|
||
if (feature) {
|
||
var nodeid = feature.getId();
|
||
if (nodeid != null) { gotoDevice(nodeid, 10); } // Goto general info tab
|
||
else { // For pointer
|
||
var nodeFeatgoto = getCorrespondingFeature(feature); gotoDevice(nodeFeatgoto.getId(), 10);
|
||
}
|
||
}
|
||
});
|
||
|
||
// On hover feature show the name of the node. Also add pointer style
|
||
xxmap.map.on('pointermove', function(evt) {
|
||
var feature = xxmap.map.forEachFeatureAtPixel(evt.pixel, function(feat, layer) { return feat; });
|
||
if (feature) {
|
||
xxmap.map.getTargetElement().style.cursor = 'pointer';
|
||
var coord = feature.getGeometry().getCoordinates();
|
||
// map_cm_popup.setPosition(evt.coordinate);
|
||
map_cm_popup.setPosition(coord);
|
||
var featid = feature.getId();
|
||
if (featid) {
|
||
QH('xmap-info-window', feature.get('name'));
|
||
} else {
|
||
var nodeFeat = getCorrespondingFeature(feature); // Return the node feature associated to pointer.
|
||
QH('xmap-info-window', nodeFeat.get('name'));
|
||
}
|
||
} else {
|
||
xxmap.map.getTargetElement().style.cursor = '';
|
||
QH('xmap-info-window', '');
|
||
}
|
||
});
|
||
|
||
// Initialize context menu for openlayers
|
||
var contextmenu = new ContextMenu({
|
||
width: 160,
|
||
defaultItems: false, // defaultItems are Zoom In/Zoom Out
|
||
items: contextmenu_items
|
||
});
|
||
|
||
// On right click open the context menu
|
||
contextmenu.on("open", function (evt) {
|
||
var feature = xxmap.map.forEachFeatureAtPixel(evt.pixel, function(ft, l){ return ft; });
|
||
xxmap.contextmenu.clear(); //Clear the context menu
|
||
if (feature) {
|
||
var featId = feature.getId();
|
||
if (featId) { addContextMenuItems(feature); } // Node feature will have an id
|
||
else { // If the feature is a pointer, Get its corresponding Node feature
|
||
var nodeFeature = getCorrespondingFeature(feature); //return the node feature associated to pointer.
|
||
if (nodeFeature) { addContextMenuItems(nodeFeature); }
|
||
else{ xxmap.contextmenu.extend(contextmenu_items); }
|
||
}
|
||
}
|
||
else { xxmap.contextmenu.extend(contextmenu_items); }
|
||
});
|
||
if (xxmap.contextmenu == null) { xxmap.contextmenu = contextmenu; }
|
||
xxmap.map.addControl(xxmap.contextmenu);
|
||
//addMeshOptions(); // Adds Mesh names to mesh dropdown
|
||
} catch (ex) {
|
||
console.log(ex);
|
||
QV('viewselectmapoption', false);
|
||
QV('devViewButton4', false);
|
||
xxmap = null;
|
||
}
|
||
}
|
||
|
||
// Add feature on to Map for a Node
|
||
function addFeature(node, lat, lon) {
|
||
var existingfeature = getModifiedFeature(node._id); // Check if Corresponding feature was Modified ( Modifed feature are in active interactions list)
|
||
if (existingfeature) { xxmap.markersSource.addFeature(existingfeature); } // Add that existing feature
|
||
else { // Add new feature for this node
|
||
if (!lat && !lon) { var loc = map_parseNodeLoc(node); lat = loc[0]; lon = loc[1]; }
|
||
|
||
// Fix the longiture and send an event to patch the db to correct coordinate format. It will cause second unnecessary updateFeature on this node to the map.
|
||
if (lon > 180) { lon = 180 - lon; meshserver.send({ action: 'changedevice', nodeid: node._id, userloc: [ lat, lon ] }); }
|
||
|
||
if ((lat < 90) && (lat > -90) && (lon < 180) && (lon > -180)) { // Check valid lat/lon
|
||
var feature = new ol.Feature({ geometry: new ol.geom.Point(ol.proj.transform([lon, lat], 'EPSG:4326','EPSG:3857')), name: node.name, status: node.conn, lat: lat, lon: lon });
|
||
feature.setId(node._id); // Set id for the device as nodeid
|
||
feature.setStyle(markerStyle(node));
|
||
xxmap.markersSource.addFeature(feature); // Add the feature to Marker Source
|
||
}
|
||
}
|
||
}
|
||
|
||
// Removing any feature from map
|
||
function removeFeature(node) {
|
||
var feature = xxmap.markersSource.getFeatureById(node._id);
|
||
if (feature) { xxmap.markersSource.removeFeature(feature); }
|
||
}
|
||
|
||
// Update feature
|
||
function updateFeature(node, feature) {
|
||
if (node.conn != feature.get('status') ) { // Update status if changed
|
||
feature.set('status',node.conn)
|
||
feature.setStyle(markerStyle(node));
|
||
}
|
||
|
||
// Since this is IP address location, add some fixed randomness to the location. Avoid pin pile-up.
|
||
var loc = map_parseNodeLoc(node);
|
||
if (loc != null) {
|
||
var lat = loc[0], lon = loc[1];
|
||
if ((lat != feature.get('lat')) || (lon != feature.get('lon'))) { // Update lat and lon if changed
|
||
feature.set('lat', lat); feature.set('lon', lon);
|
||
var modifiedCoordinates = ol.proj.transform([parseFloat(lon), parseFloat(lat)], 'EPSG:4326', 'EPSG:3857');
|
||
feature.getGeometry().setCoordinates(modifiedCoordinates);
|
||
}
|
||
}
|
||
|
||
if (node.name != feature.get('name') ) { feature.set('name', node.name); } // Update name
|
||
}
|
||
|
||
// Enable dragging of a marker after edit option is clicked in context menu
|
||
function modifyMarkerloc(ft){
|
||
var featid = ft.getId();
|
||
if (featid) {
|
||
ft.setStyle(markerStyle(getNodeFromId(ft.a), 4)); // Switch to a user marker
|
||
if ( !getActiveInteractions(ft)) {
|
||
var dragInteration = new ol.interaction.Modify({
|
||
features: new ol.Collection([ft]),
|
||
pixelTolerance: 10
|
||
});
|
||
xxmap.activeInteractions.push({ featureid: featid, feature:ft, interaction: dragInteration }); // Also keep track of Interactions
|
||
xxmap.map.addInteraction(dragInteration);
|
||
}
|
||
}
|
||
}
|
||
|
||
// This will be called when save location option is clicked in context menu
|
||
function saveMarkerloc(ft){
|
||
var featid = ft.getId()
|
||
if (featid) {
|
||
var actInteraction = getActiveInteractions(ft);
|
||
if (actInteraction) { // Check if the interaction exists
|
||
xxmap.map.removeInteraction(actInteraction); //Clear Interaction for that node
|
||
removeInteraction(featid);
|
||
var coord = ft.getGeometry().getCoordinates();
|
||
var v = ol.proj.transform(coord, 'EPSG:3857', 'EPSG:4326');
|
||
if (v[0] > 180) { v[0] = 180 - v[0]; }
|
||
var vx = [ v[1], v[0] ]; // Flip the coordinates around, lat/long
|
||
meshserver.send({ action: 'changedevice', nodeid: featid, userloc: vx }); // Send them to server to save changes
|
||
}
|
||
}
|
||
}
|
||
|
||
// Style the Markers
|
||
function markerStyle(node, type) {
|
||
if (type == null) {
|
||
type = 0;
|
||
if (node.iploc) { type = 1; }
|
||
if (node.wifiloc) { type = 2; }
|
||
if (node.gpsloc) { type = 3; }
|
||
if (node.userloc) { type = 4; }
|
||
}
|
||
var types = ['', '-ip','-wifi','-gps','-user'];
|
||
var color = connStateColor(node);
|
||
var style = new ol.style.Style({
|
||
image: new ol.style.Icon({ color: color, anchor: [0.5, 1], src: 'images/mapmarker' + types[type] + '.png' })
|
||
//stroke: new ol.style.Stroke({ color: '#000', width: 20 })
|
||
//text: new ol.style.Text({ text: 'bob!', textAlign: 'right', offsetX: -10, fill: new ol.style.Fill({ color: '#000' }), stroke: new ol.style.Stroke({ color: '#fff', width: 2 }) })
|
||
});
|
||
|
||
/*
|
||
deviceMark.setStyle(new ol.style.Style({
|
||
text: new ol.style.Text({
|
||
//font: '12px helvetica,sans-serif',
|
||
text: currentNode.name,
|
||
textAlign: 'right',
|
||
offsetX: -10,
|
||
fill: new ol.style.Fill({ color: '#000' }),
|
||
stroke: new ol.style.Stroke({ color: '#fff', width: 2 })
|
||
}),
|
||
image: new ol.style.Icon(({ color: [113, 140, 0], src: 'images/dot.png' })) }));
|
||
*/
|
||
|
||
return [ style ];
|
||
}
|
||
|
||
// TODO: Add more connection status types. Currently we only change color if connection status changes
|
||
function connStateColor(nodeConn){
|
||
if (nodeConn.conn == 1 || nodeConn.conn == 3 || nodeConn.conn == 5) { return '#00ffdd'; } // Green for connected devices
|
||
return '#C70039'; // Red if the Agent is not connected
|
||
}
|
||
|
||
// Add save/edit option to context menu
|
||
function addContextMenuItems(feature) {
|
||
if (getActiveInteractions(feature)) { // If this feature is modified then display save option in contextmenu
|
||
map_cm_saveMarker.data = feature;
|
||
xxmap.contextmenu.push(map_cm_saveMarker);
|
||
} else {
|
||
map_cm_editMarker.data = feature;
|
||
xxmap.contextmenu.push(map_cm_editMarker);
|
||
var node = getNodeFromId(feature.a);
|
||
if (node.userloc) {
|
||
map_cm_clearMarker.data = feature;
|
||
xxmap.contextmenu.push(map_cm_clearMarker);
|
||
}
|
||
}
|
||
map_cm_nodemenu_items.forEach(function (item){
|
||
if (item.text == 'Zoom-in to extent' || item.text == 'Zoom-out to extent') { item.data = feature; }
|
||
else { if (item != "-") { item.data = feature.getId(); } }
|
||
});
|
||
xxmap.contextmenu.extend(map_cm_nodemenu_items);
|
||
}
|
||
|
||
// Return a active Interaction if it exists in activeInteractions list
|
||
function getActiveInteractions(feature) {
|
||
var featid = feature.getId();
|
||
for (var i = 0; i < xxmap.activeInteractions.length; i++) {
|
||
if (xxmap.activeInteractions[i].featureid == featid) { return xxmap.activeInteractions[i].interaction; }
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// Return Modified feature based on Id
|
||
function getModifiedFeature(featid) {
|
||
if (featid) {
|
||
for (var i = 0; i < xxmap.activeInteractions.length; i++) {
|
||
if (xxmap.activeInteractions[i].featureid == featid) { return xxmap.activeInteractions[i].feature; }
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
// Remove Interaction
|
||
function removeInteraction(ftid) {
|
||
var index = -1;
|
||
for (var i = 0; i < xxmap.activeInteractions.length; i++) {
|
||
if (xxmap.activeInteractions[i].featureid === ftid) { index = i; break; }
|
||
}
|
||
if (index >= 0) { xxmap.activeInteractions.splice(index, 1); }
|
||
}
|
||
|
||
// Check if pointer coordinates are equal to features and return node feature
|
||
function getCorrespondingFeature(pointerFeat) {
|
||
var pointerCoord = pointerFeat.getGeometry().getCoordinates();
|
||
for (var i = 0; i < xxmap.activeInteractions.length ; i++) {
|
||
var modifiedFeatures = xxmap.activeInteractions[i].feature;
|
||
var fearCoord = modifiedFeatures.getGeometry().getCoordinates();
|
||
if (fearCoord[0].toFixed(5) == pointerCoord[0].toFixed(5) && fearCoord[1].toFixed(5) == pointerCoord[1].toFixed(5) ) { return modifiedFeatures; }
|
||
}
|
||
return null;
|
||
}
|
||
|
||
// Refresh the map and clear list
|
||
function refreshMap(reset, rebound){
|
||
if (reset) {
|
||
xxmap.map.setTarget(null);
|
||
xxmap.map = null;
|
||
xxmap.markersSource = null;
|
||
xxmap.mapView = null;
|
||
xxmap.mapLayer = null;
|
||
xxmap.activeInteractions = []; // Clear Active Interaction list
|
||
}
|
||
//clearMeshOptions();
|
||
//onSelectMeshChange();
|
||
var box = updateMapMarkers();
|
||
if ((box != null) && (rebound || (box[4] == 1))) {
|
||
var clat = (box[0] + box[2]) / 2;
|
||
var clon = (box[1] + box[3]) / 2;
|
||
var cscale = Math.max(Math.abs(box[0] - box[2]), Math.abs(box[1] - box[3]));
|
||
var view = xxmap.map.getView();
|
||
view.setCenter(ol.proj.transform([clon, clat], 'EPSG:4326', 'EPSG:3857'));
|
||
var i = 360, j = -2;
|
||
while (i > cscale) { j++; i = i / 2; }
|
||
view.setZoom(j);
|
||
}
|
||
}
|
||
|
||
// Called When Place a node option is clicked from context menu
|
||
function placeNode(coords) {
|
||
if (xxdialogMode) return;
|
||
var x = '<div style=margin-bottom:6px><label for=selectnode-search>Search</label>  <input type=text placeholder="Device name" id="selectnode-search" onchange=onPlaceNodeInputChange() onkeyup=onPlaceNodeInputChange() autocomplete=off style=width:120px></div><div id=placenode style="height:254px;overflow-y:auto;width:100%;margin:12px 1px 4px 1px;"><div id=noNodesMapPlace style=text-align:center;width:100%;display:none>No devices found.</div>';
|
||
for (var i in nodes) {
|
||
x += '<div class=noselect id=' + nodes[i]._id + '-rowid onclick=selectNodeToPlace(event,\''+ nodes[i]._id +'\') style=background-color:lightgray;margin-bottom:4px;border-radius:2px><input name=PlaceMapDeviceCheckbox id=' + nodes[i]._id + '-checkid type=checkbox style=width:16px;display:inline />';
|
||
x += '<div class=j' + nodes[i].icon + ' style=width:16px;height:16px;margin-top:2px;margin-right:4px;display:inline-block></div><div style=width:16px;display:inline>' + nodes[i].name + '</div></div>';
|
||
}
|
||
setDialogMode(2, "Select a node to place", 3, placeNodeEx, x + '</div>', coords);
|
||
onPlaceNodeInputChange();
|
||
}
|
||
|
||
function placeNodeEx(button, coords) {
|
||
var elements = document.getElementsByName("PlaceMapDeviceCheckbox");
|
||
for (var i in elements) {
|
||
if (elements[i].checked) {
|
||
var node = getNodeFromId(elements[i].id.substring(0, elements[i].id.length - 8));
|
||
if (node) {
|
||
var feature = xxmap.markersSource.getFeatureById(i);
|
||
var v = ol.proj.transform(coords, 'EPSG:3857', 'EPSG:4326');
|
||
var vx = [ v[1], v[0] ]; // Flip the coordinates around, lat/long
|
||
if (feature) {
|
||
feature.getGeometry().setCoordinates(coords);
|
||
var activeInteraction = getActiveInteractions(feature);
|
||
if (activeInteraction) {
|
||
saveMarkerloc(feature);
|
||
} else { // If this feature is not saved after its location is changed, then send updated coords to server.
|
||
meshserver.send({ action: 'changedevice', nodeid: node._id, userloc: vx }); // Send them to server to save changes
|
||
}
|
||
} else {
|
||
meshserver.send({ action: 'changedevice', nodeid: node._id, userloc: vx }); // This Node is not yet added to maps.
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Called when the user changes the search box
|
||
function onPlaceNodeInputChange() {
|
||
updatePlaceNodeTable(Q('selectnode-search').value.trim().toLowerCase());
|
||
}
|
||
|
||
// Update the list of devices in the "place on map" table
|
||
function updatePlaceNodeTable(inputSearch) {
|
||
var elements = document.getElementsByName("PlaceMapDeviceCheckbox"), count = 0;
|
||
for (var i in nodes) {
|
||
var visible = ((nodes[i].namel.indexOf(inputSearch) >= 0 || inputSearch == '') || (nodes[i].rnamel != null && nodes[i].rnamel.indexOf(inputSearch) >= 0));
|
||
if (visible) { count++; }
|
||
QV(nodes[i]._id + '-rowid', visible);
|
||
}
|
||
QV('noNodesMapPlace', count == 0);
|
||
/*
|
||
console.log(selected);
|
||
for (var i in nodes) {
|
||
if ((nodes[i].name.toLowerCase().indexOf(inputSearch) >= 0 || inputSearch == '') || (nodes[i].rnamel != null && nodes[i].rnamel.toLowerCase().indexOf(inputSearch) >= 0)) {
|
||
console.log(selected.indexOf(nodes[i]._id));
|
||
x += '<div class=noselect id=' + nodes[i]._id + '-rowid onclick=selectNodeToPlace(event,\''+ nodes[i]._id +'\') style=background-color:lightgray;margin-bottom:4px;border-radius:2px><input name=PlaceMapDeviceCheckbox id=' + nodes[i]._id + '-checkid type=checkbox style=width:16px;display:inline ' + ((selected.indexOf(nodes[i]._id) >= 0)?'checked':'') + ' />';
|
||
x += '<div class=j' + nodes[i].icon + ' style=width:16px;height:16px;margin-top:2px;margin-right:4px;display:inline-block></div><div style=width:16px;display:inline>' + nodes[i].name + '</div></div>';
|
||
}
|
||
}
|
||
if (x == '') { x = '<div style=text-align:center;width:100%>No devices found.</div>'; }
|
||
QH('placenode', '');
|
||
*/
|
||
}
|
||
|
||
// Called when a user clicks on a device to toggle selection for placement on map.
|
||
function selectNodeToPlace(e, id) {
|
||
// Toggle checkbox if needed
|
||
if (e.target.name != 'PlaceMapDeviceCheckbox') { var inputElement = Q(id + '-checkid'); inputElement.checked = !inputElement.checked; }
|
||
|
||
// Check button state
|
||
var elements = document.getElementsByName("PlaceMapDeviceCheckbox"), checkcount = 0;
|
||
for (var i in elements) { if (elements[i].checked) checkcount++; }
|
||
QE('idx_dlgOkButton', checkcount > 0);
|
||
}
|
||
|
||
// Add option for available meshes in mesh Dropdown
|
||
function addMeshOptions(addMeshid, meshName) {
|
||
/*
|
||
var meshOptions = Q('select-mesh');
|
||
if (addMeshid && meshName) {
|
||
var option = document.createElement('option');
|
||
option.value =addMeshid;
|
||
option.text = meshName;
|
||
meshOptions.add(option); // Add specific option
|
||
}
|
||
else {
|
||
for (var i in meshes) { // Add all options
|
||
var option = document.createElement('option');
|
||
option.value = i;
|
||
option.text = meshes[i].name;
|
||
meshOptions.add(option);
|
||
}
|
||
}
|
||
*/
|
||
}
|
||
|
||
// Remove/Modify options in Mesh dropdown (if modMeshname is defined then Modify else Remove)
|
||
function meshOptionRmvMod(delMeshid, modMeshname){
|
||
/*
|
||
var meshOptions = Q('select-mesh');
|
||
if (delMeshid) {
|
||
var index=-1;
|
||
for (var i = 1; i < meshOptions.options.length; i++) {
|
||
if (meshOptions[i].value === delMeshid) { index=i; }
|
||
}
|
||
if (index > 0) {
|
||
if (modMeshname) {
|
||
meshOptions[index].innerHTML=modMeshname; // If Mesh name is Modified
|
||
}
|
||
else { meshOptions.remove(index); }
|
||
}
|
||
}
|
||
*/
|
||
}
|
||
|
||
//Check if there is any mesh created
|
||
function meshExists() {
|
||
for (var i in meshes) { if (meshes[i]) { return true; } }
|
||
return false;
|
||
}
|
||
|
||
// Reset Mesh dropdown option to 'All' when a current view mesh is deleted.
|
||
function setMeshView(emeshid) {
|
||
var selectMeshElement=Q("select-mesh");
|
||
var selectedIndex = selectMeshElement.selectedIndex;
|
||
if (selectMeshElement[selectedIndex].value == emeshid) { selectMeshElement[0].selected = true; onSelectMeshChange(); }
|
||
}
|
||
|
||
// Clear all mesh options except 'All'
|
||
function clearMeshOptions() {
|
||
/*
|
||
var meshOptions=Q('select-mesh');
|
||
for(var i = meshOptions.options.length - 1 ; i > 0 ; i--) { meshOptions.remove(i); }
|
||
*/
|
||
}
|
||
|
||
// Make a http get call- Replace this with AJAX get if jquery is used
|
||
function getSearchLocation() {
|
||
try {
|
||
var searchdata = Q('mapSearchLocation').value.trim();
|
||
if (searchdata.length > 0) {
|
||
var xmlhttp = new XMLHttpRequest(); // Compatible with Chrome, Opera, Safari, IE7+, Firefox.
|
||
xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { formatSearchData(xmlhttp.responseText); } }
|
||
xmlhttp.open("GET", 'https://nominatim.openstreetmap.org/search?q=' + searchdata + '&format=json', true); // Get request
|
||
xmlhttp.send();
|
||
}
|
||
} catch (e) {}
|
||
}
|
||
|
||
// Format data recieved from nominatim API and display it on content window
|
||
function formatSearchData(data) {
|
||
try {
|
||
QH('xmapSearchResults','');
|
||
var dataInfo = JSON.parse(data), count = 0, x = '<div class="xmapItem">';
|
||
for (var i = 0; i < dataInfo.length; i++) {
|
||
if (dataInfo[i].display_name && dataInfo[i].boundingbox[0] && dataInfo[i].boundingbox[1] && dataInfo[i].boundingbox[2] && dataInfo[i].boundingbox[3]) {
|
||
count++;
|
||
var itemclass = (i % 2 == 0)?'xmapItemSel1':'xmapItemSel1';
|
||
x += '<div class="' + itemclass + '" onclick=mapGotoSelectedLocation(this)><div>' + dataInfo[i].display_name + '</div><div style=display:none>' + dataInfo[i].boundingbox[0] + '!#!' + dataInfo[i].boundingbox[1] + '!#!' + dataInfo[i].boundingbox[2] + '!#!' + dataInfo[i].boundingbox[3] + '</div></div>';
|
||
}
|
||
}
|
||
x += '</div>';
|
||
if (count == 1) {
|
||
// If only one result is returned then zoom to that location
|
||
var extent = [ parseFloat(dataInfo[0].boundingbox[2]), parseFloat(dataInfo[0].boundingbox[0]), parseFloat(dataInfo[0].boundingbox[3]), parseFloat(dataInfo[0].boundingbox[1]) ];
|
||
zoomToExtent(extent);
|
||
} else {
|
||
if (count == 0) { x = '<div style=width:200px>No location found.<div>'; }
|
||
QV('xmapSearchResultsDlg', true);
|
||
}
|
||
QH('xmapSearchResults', x);
|
||
}
|
||
catch (e) {}
|
||
}
|
||
|
||
// Zoom into the bounding box
|
||
function mapGotoSelectedLocation(obj) {
|
||
var objchildren = obj.children;
|
||
var boundingBox = objchildren[1].innerHTML.split('!#!');
|
||
var extent = [parseFloat(boundingBox[2]), parseFloat(boundingBox[0]), parseFloat(boundingBox[3]), parseFloat(boundingBox[1])];
|
||
//Q('search-location').value = objchildren[0].innerHTML;
|
||
zoomToExtent(extent);
|
||
mapCloseSearchWindow();
|
||
}
|
||
|
||
// Close the search window
|
||
function mapCloseSearchWindow() {
|
||
QH('xmapSearchResults', '');
|
||
QV('xmapSearchResultsDlg', false);
|
||
}
|
||
|
||
// Zoom to specific cordinates
|
||
function zoomToLocation(coordinates, zoomVal) {
|
||
var view = xxmap.map.getView();
|
||
view.setCenter(coordinates);
|
||
view.setZoom(zoomVal);
|
||
}
|
||
|
||
function zoomToFitExtent() {
|
||
var features = xxmap.markersSource.getFeatures();
|
||
if (features.length > 0) {
|
||
var extent = xxmap.markersSource.getExtent();
|
||
xxmap.map.getView().fit(extent, xxmap.map.getSize());
|
||
}
|
||
}
|
||
|
||
function zoomToExtent(extent){
|
||
var boundingExtent = ol.proj.transformExtent(extent, ol.proj.get('EPSG:4326'), ol.proj.get('EPSG:3857'));
|
||
xxmap.map.getView().fit(boundingExtent, xxmap.map.getSize());
|
||
}
|
||
|
||
|
||
//
|
||
// MY DEVICE
|
||
//
|
||
function refreshDevice(nodeid) {
|
||
if (!currentNode || currentNode._id != nodeid) return;
|
||
gotoDevice(nodeid, xxcurrentView, true);
|
||
}
|
||
|
||
function getNodeRights(nodeid) {
|
||
var node = getNodeFromId(nodeid), mesh = meshes[node.meshid];
|
||
return mesh.links[userinfo._id].rights;
|
||
}
|
||
|
||
var currentNode;
|
||
var powerTimelineNode = null;
|
||
var powerTimelineReq = null;
|
||
var powerTimelineUpdate = null;
|
||
var powerTimeline = null;
|
||
function getCurrentNode() { return currentNode; };
|
||
function gotoDevice(nodeid, panel, refresh, event) {
|
||
// 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\" tab 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))) { 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\" tab and look at the \"Account Security\" section."); return; }
|
||
|
||
if (event && (event.shiftKey == true)) {
|
||
// Open the device in a different tab
|
||
window.open(window.location.origin + '?node=' + nodeid.split('/')[2] + '&viewmode=10&hide=16', 'meshcentral:' + nodeid);
|
||
return;
|
||
}
|
||
|
||
//disconnectAllKvmFunction();
|
||
var node = getNodeFromId(nodeid);
|
||
var mesh = meshes[node.meshid];
|
||
var meshrights = mesh.links[userinfo._id].rights;
|
||
if (!currentNode || currentNode._id != node._id || refresh == true) {
|
||
currentNode = node;
|
||
|
||
// Add node name
|
||
var nname = EscapeHtml(node.name);
|
||
if (nname.length == 0) { nname = '<i>None</i>'; }
|
||
if (((meshrights & 4) != 0) && ((!mesh.flags) || ((mesh.flags & 2) == 0))) { nname = '<span title="Click here to edit the server-side device name" onclick=showEditNodeValueDialog(0) style=cursor:pointer>' + nname + ' <img class=hoverButton src="images/link5.png" /></span>'; }
|
||
QH('p10deviceName', nname);
|
||
QH('p11deviceName', nname);
|
||
QH('p12deviceName', nname);
|
||
QH('p13deviceName', nname);
|
||
QH('p14deviceName', nname);
|
||
QH('p15deviceName', 'Console - ' + nname);
|
||
QH('p16deviceName', nname);
|
||
|
||
// Node attributes
|
||
var x = '<table style=width:100%>';
|
||
|
||
// Attribute: Mesh
|
||
x += addDeviceAttribute('<span title="The name of the device group this computer belong to.">Group</span>', '<a title="The name of the device group this computer belong to" onclick=gotoMesh("' + node.meshid + '") style=cursor:pointer>' + EscapeHtml(meshes[node.meshid].name) + '</a>');
|
||
|
||
// Attribute: Name
|
||
if ((node.rname != null) && (node.name != node.rname)) { x += addDeviceAttribute('<span title="The name of this computer as set in the operating system">Name</span>', '<span title="The name of this computer as set in the operating system">' + 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 + ' <img class=hoverButton src="images/link5.png" /></span>');
|
||
} else {
|
||
x += addDeviceAttribute('Description', description);
|
||
}
|
||
|
||
// Attribute: Mesh Agent
|
||
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'];
|
||
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('Mesh Agent', str);
|
||
}
|
||
|
||
// Attribute: Intel AMT
|
||
if (node.intelamt != null) {
|
||
var str = '';
|
||
var provisioningStates = { 0: 'Not Activated (Pre)', 1: 'Not Activated (In)', 2: 'Activated' };
|
||
if (node.intelamt.ver != null && node.intelamt.state == null) { str += '<i>Unknown State</i>, v' + 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.state == 2) && node.intelamt.flags) { if (node.intelamt.flags & 2) { str += ' <span title="Intel AMT is activated in Client Control Mode">CCM</span>'; } else if (node.intelamt.flags & 4) { str += ' <span title="Intel AMT is activated in Admin Control Mode">ACM</span>'; } }
|
||
str += (', v' + node.intelamt.ver);
|
||
}
|
||
|
||
if (node.intelamt.tls == 1) { str += ', <span title="Intel AMT is setup with TLS network security">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 title="Edit Intel® AMT credentials" onclick=editDeviceAmtSettings("' + node._id + '")>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 title="Edit Intel® AMT credentials" style=cursor:pointer onclick=editDeviceAmtSettings("' + node._id + '")>';
|
||
}
|
||
}
|
||
x += addDeviceAttribute('Intel® AMT', str);
|
||
}
|
||
|
||
// Attribute: Mesh Agent Tag
|
||
if ((node.agent != null) && (node.agent.tag != null) && (node.agent.tag != 'mailto:')) {
|
||
var tag = EscapeHtml(node.agent.tag);
|
||
if (tag.startsWith('mailto:')) { tag = '<a href="' + tag + '">' + tag.substring(7) + '</a>'; }
|
||
x += addDeviceAttribute('Agent Tag', tag);
|
||
}
|
||
|
||
// Attribute: Intel AMT
|
||
//if (node.intelamt && node.intelamt.user) { x += addDeviceAttribute('Intel® AMT', node.intelamt.user); }
|
||
|
||
// Operating system description
|
||
if (node.osdesc) { x += addDeviceAttribute('Operating System', node.osdesc); }
|
||
|
||
// Active Users
|
||
if (node.users && node.conn && (node.users.length > 0) && (node.conn & 1)) { x += addDeviceAttribute('Active User' + ((node.users.length > 1)?'s':''), node.users.join(', ')); }
|
||
|
||
// 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 title="Mesh agent is connected and ready for use.">Mesh Agent</span>');
|
||
if ((node.conn & 2) != 0) cstate.push('<span title="Intel® AMT CIRA is connected and ready for use.">Intel® AMT CIRA</span>');
|
||
else if ((node.conn & 4) != 0) cstate.push('<span title="Intel® AMT is routable and ready for use.">Intel® AMT</span>');
|
||
if ((node.conn & 8) != 0) cstate.push('<span title="Mesh agent is reachable using another agent as relay.">Mesh Relay</span>');
|
||
x += addDeviceAttribute('Connectivity', cstate.join(', '));
|
||
}
|
||
|
||
// Node grouping tags
|
||
var groupingTags = '<i>None</i>';
|
||
if (node.tags != null) { groupingTags = ''; for (var i in node.tags) { groupingTags += '<span class="tagSpan">' + node.tags[i] + '</span>'; } }
|
||
if ((meshrights & 4) != 0) {
|
||
x += addDeviceAttribute('Tags', '<span onclick=showEditNodeValueDialog(3) style=cursor:pointer>' + groupingTags + ' <img class=hoverButton src="images/link5.png" /></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 title="Perform power actions on the device" onclick=deviceActionFunction() />'; }
|
||
x += '<input type=button value=Notes title="View notes about this device" onclick=showNotes(' + ((meshrights & 128) == 0) + ',"' + encodeURIComponent(node._id) + '") />';
|
||
//if ((connectivity & 1) && (meshrights & 8) && (node.agent.id < 5)) { x += '<input type=button value=Toast title="Display a text message of the remote device" onclick=deviceToastFunction() />'; }
|
||
QH('p10html', x);
|
||
|
||
// Show node last 7 days timeline
|
||
masterUpdate(256);
|
||
|
||
// Show bottom buttons
|
||
x = '<div class="p10html3right">';
|
||
if ((meshrights & 4) != 0) {
|
||
// TODO: Show change group only if there is another mesh of the same type.
|
||
x += ' <a onclick=p10showChangeGroupDialog(["' + node._id + '"]) title="Move this device to a different device group">Change Group</a>';
|
||
x += ' <a onclick=p10showDeleteNodeDialog("' + node._id + '") title="Remove this device">Delete Device</a>';
|
||
}
|
||
x += '</div><div class="p10html3left">';
|
||
if (mesh.mtype == 2) x += '<a onclick=p10showNodeNetInfoDialog("' + node._id + '") title="Show device network interface information">Interfaces</a> ';
|
||
if (xxmap != null) x += '<a onclick=p10showNodeLocationDialog("' + node._id + '") title="Show device locations information">Location</a> ';
|
||
if (((meshrights & 8) != 0) && (mesh.mtype == 2)) x += '<a onclick=p10showMeshCmdDialog(1,"' + node._id + '") title="Traffic router used to connect to a device thru this server.">Router</a> ';
|
||
|
||
// RDP link, show this link only of the remote machine is Windows.
|
||
if (((connectivity & 1) != 0) && (clickOnce == true) && (mesh.mtype == 2) && ((meshrights & 8) != 0)) {
|
||
if ((node.agent.id > 0) && (node.agent.id < 5)) { x += '<a onclick=p10clickOnce("' + node._id + '","RDP2",3389) title="Requires Microsoft ClickOnce support in your browser.">RDP</a> '; }
|
||
if (node.agent.id > 4) {
|
||
x += '<a onclick=p10clickOnce("' + node._id + '","PSSH",22) title="Requires Microsoft ClickOnce support in your browser.">Putty</a> ';
|
||
x += '<a onclick=p10clickOnce("' + node._id + '","WSCP",22) title="Requires Microsoft ClickOnce support in your browser.">WinSCP</a> ';
|
||
}
|
||
}
|
||
x += '</div><br>'
|
||
|
||
QH('p10html3', x);
|
||
|
||
// Set the node power state
|
||
var powerstate = PowerStateStr(node.state);
|
||
//if (node.state == 0) { powerstate = 'Unknown State'; }
|
||
if ((connectivity & 1) != 0) { if (powerstate.length > 0) { powerstate += '<br/>'; } powerstate += '<span style=font-size:12px title="Agent connected">Agent connected</span>'; }
|
||
if ((connectivity & 2) != 0) { if (powerstate.length > 0) { powerstate += '<br/>'; } powerstate += '<span style=font-size:12px title="Intel® AMT connected">Intel® AMT connected</span>'; }
|
||
else if ((connectivity & 4) != 0) { if (powerstate.length > 0) { powerstate += '<br/>'; } powerstate += '<span style=font-size:12px title="Intel® AMT detected">Intel® AMT detected</span>'; }
|
||
if ((powerstate == '') && node.lastconnect) { powerstate = '<span style=font-size:12px>Last seen:<br />' + printDateTime(new Date(node.lastconnect)) + '</span>'; }
|
||
QH('MainComputerState', powerstate);
|
||
|
||
// Set the node icon
|
||
Q('MainComputerImage').setAttribute("src", "images/icons256-" + node.icon + "-1.png");
|
||
Q('MainComputerImage').className = ((!node.conn) || (node.conn == 0)?'gray':'');
|
||
|
||
// Check if we have terminal and file access
|
||
var terminalAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 512) == 0));
|
||
var fileAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 1024) == 0));
|
||
var amtAccess = ((meshrights == 0xFFFFFFFF) || ((meshrights & 2048) == 0));
|
||
|
||
// Setup/Refresh the desktop tab
|
||
if (terminalAccess) { setupTerminal(); }
|
||
if (fileAccess) { setupFiles(); }
|
||
var consoleRights = ((meshrights & 16) != 0);
|
||
if (consoleRights) { setupConsole(); } else { if (panel == 15) { panel = 10; } }
|
||
|
||
// Show or hide the tabs
|
||
// mesh.mtype: 1 = Intel AMT only, 2 = Mesh Agent
|
||
// node.agent.caps (bitmask): 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console
|
||
QV('MainDevDesktop', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 1) != 0) || (node.intelamt && (node.intelamt.state == 2))) && ((meshrights & 8) || (meshrights & 256)));
|
||
QV('MainDevTerminal', ((mesh.mtype == 1) || (node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 2) != 0) || (node.intelamt && (node.intelamt.state == 2))) && (meshrights & 8) && terminalAccess);
|
||
QV('MainDevFiles', ((mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 4) != 0))) && (meshrights & 8) && fileAccess);
|
||
QV('MainDevAmt', (node.intelamt != null) && ((node.intelamt.state == 2) || (node.conn & 2)) && (meshrights & 8) && amtAccess);
|
||
QV('MainDevConsole', (consoleRights && (mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 8) != 0))) && (meshrights & 8));
|
||
QV('p15uploadCore', (node.agent != null) && (node.agent.caps != null) && ((node.agent.caps & 16) != 0));
|
||
QH('p15coreName', ((node.agent != null) && (node.agent.core != null))?node.agent.core:'');
|
||
|
||
// Setup/Refresh Intel AMT tab
|
||
var amtFrameNode = Q('p14iframe').contentWindow.getCurrentMeshNode();
|
||
if ((amtFrameNode != null) && (amtFrameNode._id != currentNode._id)) { Q('p14iframe').contentWindow.disconnect(); }
|
||
var online = ((node.conn & 6) != 0)?true:false; // If CIRA (2) or AMT (4) connected, enable Commander
|
||
Q('p14iframe').contentWindow.setConnectionState(online);
|
||
Q('p14iframe').contentWindow.setFrameHeight('650px');
|
||
Q('p14iframe').contentWindow.setAuthCallback(updateAmtCredentials);
|
||
|
||
// Display "action" button on desktop/terminal/files
|
||
QV('deskActionsBtn', (meshrights & 72) != 0); // 72 = Wake-up + Remote Control permissions
|
||
QV('termActionsBtn', (meshrights & 72) != 0);
|
||
QV('filesActionsBtn', (meshrights & 72) != 0);
|
||
|
||
// Request the power timeline
|
||
if ((powerTimelineNode != currentNode._id) && (powerTimelineReq != currentNode._id)) {
|
||
QH('p10html2', '');
|
||
powerTimelineReq = currentNode._id;
|
||
meshserver.send({ action: 'powertimeline', nodeid: currentNode._id });
|
||
meshserver.send({ action: 'lastconnect', nodeid: currentNode._id });
|
||
}
|
||
|
||
// Reset the desktop tools
|
||
QV('DeskTools', false);
|
||
showDeskToolsProcesses();
|
||
|
||
// Ask for device events
|
||
refreshDeviceEvents();
|
||
|
||
// Update the web page title
|
||
if ((currentNode) && (xxcurrentView >= 10) && (xxcurrentView < 20)) { document.title = decodeURIComponent("{{{extitle}}}") + ' - ' + currentNode.name; } else { document.title = decodeURIComponent("{{{extitle}}}"); }
|
||
|
||
// Clear user consent status if present
|
||
p11clearConsoleMsg();
|
||
p12clearConsoleMsg();
|
||
p13clearConsoleMsg();
|
||
}
|
||
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);
|
||
}
|
||
|
||
function showNotes(readonly, noteid) {
|
||
if (xxdialogMode) return;
|
||
setDialogMode(2, "Notes", 2, showNotesEx, '<textarea id=d2devNotes ro=' + readonly + ' noteid=' + noteid + ' readonly style=background-color:#fcf3cf;width:100%;height:200px;resize:none;overflow-y:scroll></textarea><span style=font-size:10px>Notes can be viewed and changed by other administrators.<span>', noteid);
|
||
meshserver.send({ action: 'getNotes', id: decodeURIComponent(noteid) });
|
||
}
|
||
|
||
function showNotesEx(buttons, tag) { meshserver.send({ action: 'setNotes', id: decodeURIComponent(tag), notes: encodeURIComponent(Q('d2devNotes').value) }); }
|
||
|
||
function deviceChat() {
|
||
if (xxdialogMode) return;
|
||
var url = '/messenger?id=meshmessenger/' + encodeURIComponent(currentNode._id) + '/' + encodeURIComponent(userinfo._id) + '&title=' + currentNode.name;
|
||
if ((authCookie != null) && (authCookie != '')) { url += '&auth=' + authCookie; }
|
||
window.open(url, 'meshmessenger:' + currentNode._id);
|
||
meshserver.send({ action: 'meshmessenger', nodeid: decodeURIComponent(currentNode._id) });
|
||
}
|
||
|
||
function deviceUrlFunction() {
|
||
if (xxdialogMode) return;
|
||
setDialogMode(2, "Open Page on Device", 3, deviceUrlFunctionEx, '<input id=d2devurl placeholder="http://server.com" style=width:100%;overflow-y:scroll></input>');
|
||
}
|
||
|
||
function deviceUrlFunctionEx() {
|
||
meshserver.send({ action: 'msg', type: 'openUrl', nodeid: currentNode._id, url: Q('d2devurl').value });
|
||
}
|
||
|
||
function deviceToastFunction() {
|
||
if (xxdialogMode) return;
|
||
setDialogMode(2, "Device Notification", 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 });
|
||
}
|
||
|
||
function deviceActionFunction() {
|
||
if (xxdialogMode) return;
|
||
var meshrights = meshes[currentNode.meshid].links[userinfo._id].rights;
|
||
var x = "Select an operation to perform on this device.<br /><br />";
|
||
var y = '<select id=d2deviceop style=float:right;width:250px>';
|
||
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 });
|
||
}
|
||
}
|
||
|
||
// Called when MeshCommander needs new credentials or updated credentials.
|
||
function updateAmtCredentials(forceDialog) {
|
||
var node = getNodeFromId(currentNode._id);
|
||
if ((forceDialog == true) || (node.intelamt.user == null) || (node.intelamt.user == '')) {
|
||
editDeviceAmtSettings(currentNode._id, updateAmtCredentialsEx);
|
||
} else {
|
||
Q('p14iframe').contentWindow.connectButtonfunctionEx();
|
||
}
|
||
}
|
||
|
||
function updateAmtCredentialsEx(button, tag) {
|
||
Q('p14iframe').contentWindow.connectButtonfunctionEx();
|
||
}
|
||
|
||
// 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 });
|
||
meshserver.send({ action: 'lastconnect', nodeid: currentNode._id });
|
||
}
|
||
}
|
||
|
||
// Draw device power bars. The bars are 766px wide.
|
||
function drawDeviceTimeline() {
|
||
if ((currentNode == null) || (xxcurrentView < 10) || (xxcurrentView > 19)) return;
|
||
var timeline = null, now = Date.now();
|
||
if (currentNode._id == powerTimelineNode) { timeline = powerTimeline; }
|
||
|
||
// Calculate when the timeline starts
|
||
var d = new Date();
|
||
d.setHours(0, 0, 0, 0);
|
||
d = new Date(d.getTime() - (1000 * 60 * 60 * 24 * 6));
|
||
var timelineStart = d.getTime();
|
||
|
||
// De-compact the timeline
|
||
var timeline2 = [];
|
||
if (timeline != null && timeline.length > 1) {
|
||
timeline2.push([ 0, timeline[1], timeline[0] ]); // Start, End, Power
|
||
var ct = timeline[1];
|
||
for (var i = 2; i < timeline.length; i += 2) {
|
||
var power = timeline[i], dt = now;
|
||
if (timeline.length > (i + 1)) { dt = timeline[i + 1]; }
|
||
timeline2.push([ ct, ct + dt, power ]); // Start, End, Power
|
||
ct = ct + dt;
|
||
}
|
||
}
|
||
|
||
// Draw the timeline
|
||
var x = '', count = 1, date = new Date();
|
||
var totalWidth = Q('masthead').offsetWidth - (160 + 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) {
|
||
var title = powerStateStrings2[block[2]] + ' from ' + printTime(new Date(ts)) + ' to ' + printTime(new Date(te)) + '.';
|
||
datavalue += '<div class="pwState ' + powerColor(block[2]) + '" title="' + title + '" style="width:' + width + 'px;"></div>';
|
||
}
|
||
}
|
||
}
|
||
x += '<tr class=' + (((count % 2) == 0)?'altBack':'') + '><td><div> ' + printDate(date) + '<div></div></div></td><td><div>' + datavalue + '</div></td></tr>';
|
||
++count;
|
||
date = new Date(date.getTime() - (1000 * 60 * 60 * 24)); // Substract one day
|
||
}
|
||
QH('p10html2', '<table cellpadding=2 cellspacing=0><thead><tr style=><th scope=col style=text-align:center;width:150px>Day</th><th scope=col style=text-align:center><a download href="devicepowerevents.ashx?id=' + currentNode._id + '" onclick="setDialogMode(0)"><img title="Download power events" src="images/link4.png" /></a>7 Day Power State</th></tr></thead><tbody>' + x + '</tbody></table>');
|
||
}
|
||
|
||
// Return a color for the given power state
|
||
function powerColor(x) { if (x < powerColorTable.length) { return powerColorTable[x]; } return 'pwsYellow'; }
|
||
|
||
// 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 class=style7>' + name + '</td><td class=style9>' + value + '</td></tr>'; }
|
||
|
||
function editDeviceAmtSettings(nodeid, func, arg) {
|
||
if (xxdialogMode) return;
|
||
var x = '', node = getNodeFromId(nodeid), buttons = 3, meshrights = getNodeRights(nodeid);
|
||
if ((meshrights & 4) == 0) return;
|
||
x += addHtmlValue('Username', '<input id=dp10username style=width:230px maxlength=32 autocomplete=nope placeholder="admin" onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />');
|
||
x += addHtmlValue('Password', '<input id=dp10password type=password style=width:230px autocomplete=nope maxlength=32 onchange=validateDeviceAmtSettings() onkeyup=validateDeviceAmtSettings() />');
|
||
x += addHtmlValue('Security', '<select id=dp10tls style=width:236px><option value=0>No TLS security</option><option value=1>TLS security required</option></select>');
|
||
if ((node.intelamt.user != null) && (node.intelamt.user != '')) { buttons = 7; }
|
||
setDialogMode(2, "Edit Intel® AMT credentials", buttons, editDeviceAmtSettingsEx, x, { node: node, func: func, arg: arg });
|
||
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: Q('dp10tls').value } });
|
||
tag.node.intelamt.user = amtuser;
|
||
tag.node.intelamt.tls = Q('dp10tls').value;
|
||
if (tag.func) { setTimeout(function () { tag.func(null, tag.arg); }, 300); }
|
||
}
|
||
}
|
||
|
||
function p10showChangeGroupDialog(nodeids) {
|
||
if (xxdialogMode) return;
|
||
var targetMeshId = null;
|
||
if (nodeids.length == 1) { try { targetMeshId = meshes[getNodeFromId(nodeids[0])]._id; } catch (ex) { } }
|
||
|
||
// List all available alternative groups
|
||
var y = "<select id=p10newGroup style=width:236px>", count = 0;
|
||
for (var i in meshes) {
|
||
var meshrights = meshes[i].links[userinfo._id].rights;
|
||
if ((meshes[i]._id != targetMeshId) && (meshrights & 4)) { count++; y += "<option value='" + meshes[i]._id + "'>" + meshes[i].name + "</option>"; }
|
||
}
|
||
y += "</select>";
|
||
|
||
if (count > 0) {
|
||
var x = (nodeids.length == 1) ? "Select a new group for this device<br /><br />" : "Select a new group for selected devices<br /><br />";
|
||
x += addHtmlValue('New Device Group', y);
|
||
setDialogMode(2, "Change Group", 3, p10showChangeGroupDialogEx, x, nodeids);
|
||
} else {
|
||
setDialogMode(2, "Change Group", 1, null, "No other device group of same type exists.");
|
||
}
|
||
}
|
||
|
||
function p10showChangeGroupDialogEx(b, nodeids) {
|
||
meshserver.send({ action: 'changeDeviceMesh', nodeids: nodeids, meshid: Q('p10newGroup').value });
|
||
}
|
||
|
||
function p10showDeleteNodeDialog(nodeid) {
|
||
if (xxdialogMode) return;
|
||
var x = "Are you sure you want to delete node \"" + EscapeHtml(currentNode.name) + "\"?<br /><br />";
|
||
x += "<label><input id=p10check type=checkbox onchange=p10validateDeleteNodeDialog() />Confirm</label>";
|
||
setDialogMode(2, "Delete Node", 3, p10showDeleteNodeDialogEx, x, nodeid);
|
||
p10validateDeleteNodeDialog();
|
||
}
|
||
|
||
function p10validateDeleteNodeDialog() {
|
||
QE('idx_dlgOkButton', Q('p10check').checked);
|
||
}
|
||
|
||
function p10showDeleteNodeDialogEx(buttons, nodeid) {
|
||
meshserver.send({ action: 'removedevices', nodeids: [ nodeid ] });
|
||
}
|
||
|
||
function p10clickOnce(nodeid, protocol, port) {
|
||
meshserver.send({ action: 'getcookie', nodeid: nodeid, tcpport: port, tag: 'clickonce', protocol: protocol });
|
||
}
|
||
|
||
// Show current location
|
||
var d2map = null;
|
||
function p10showNodeLocationDialog() {
|
||
if ((xxdialogMode != null) && (xxdialogTag == '@xxmap')) { setDialogMode(0); } else { if (xxdialogMode) return; }
|
||
var markers = [], types = ['iploc', 'wifiloc', 'gpsloc', 'userloc'], boundingBox = null;
|
||
|
||
for (var loctype in types) {
|
||
if (currentNode[types[loctype]] != null) {
|
||
var loc = currentNode[types[loctype]].split(','), lat = parseFloat(loc[0]), lon = parseFloat(loc[1]);
|
||
if ((lat < 90) && (lat > -90) && (lon < 180) && (lon > -180)) { // Check valid lat/lon
|
||
var deviceMark = new ol.Feature({ geometry: new ol.geom.Point(ol.proj.fromLonLat([lon, lat])) });
|
||
deviceMark.setStyle(markerStyle(currentNode, parseInt(loctype) + 1));
|
||
markers.push(deviceMark);
|
||
|
||
if (boundingBox == null) { boundingBox = [ lat, lon, lat, lon, 0 ]; } else { if (lat < boundingBox[0]) { boundingBox[0] = lat; } if (lon < boundingBox[1]) { boundingBox[1] = lon; } if (lat > boundingBox[2]) { boundingBox[2] = lat; } if (lon > boundingBox[3]) { boundingBox[3] = lon; } }
|
||
}
|
||
}
|
||
}
|
||
|
||
// Setup the device mark layer
|
||
var vectorSource = new ol.source.Vector({ features: markers });
|
||
var vectorLayer = new ol.layer.Vector({ source: vectorSource });
|
||
|
||
//var x = '<div><a href="https://www.google.com/maps/preview/@' + lat + ',' + lng + ',12z" rel="noreferrer noopener" target=_blank>Open in Google maps</a></div>';
|
||
var x = '<div id=d2map style=width:100%;height:300px></div>';
|
||
setDialogMode(2, "Device Location", 1, null, x, '@xxmap');
|
||
|
||
var clng = 0, clat = 0, zoom = 8;
|
||
if (boundingBox != null) {
|
||
var clat = (boundingBox[0] + boundingBox[2]) / 2;
|
||
var clng = (boundingBox[1] + boundingBox[3]) / 2;
|
||
var cscale = Math.max(Math.abs(boundingBox[0] - boundingBox[2]), Math.abs(boundingBox[1] - boundingBox[3]));
|
||
var i = 360, zoom = -2;
|
||
while (i > cscale) { zoom++; i = i / 2; }
|
||
}
|
||
|
||
if (markers.length == 1) { zoom = 8; }
|
||
|
||
// Setup the map
|
||
d2map = new ol.Map({
|
||
target: 'd2map',
|
||
interactions: ol.interaction.defaults({dragPan:false, mouseWheelZoom:false}),
|
||
layers: [ new ol.layer.Tile({ source: new ol.source.OSM() }), vectorLayer ],
|
||
view: new ol.View({ center: ol.proj.fromLonLat([clng, clat]), zoom: zoom })
|
||
});
|
||
}
|
||
|
||
// Show network interfaces
|
||
function p10showNodeNetInfoDialog() {
|
||
if (xxdialogMode) return;
|
||
setDialogMode(2, "Network Interfaces", 1, null, "<div id=d2netinfo>Loading...</div>", 'if' + currentNode._id );
|
||
meshserver.send({ action: 'getnetworkinfo', nodeid: currentNode._id });
|
||
}
|
||
|
||
// Show MeshCentral Router dialog
|
||
function p10showMeshRouterDialog() {
|
||
if (xxdialogMode) return;
|
||
var x = "<div>MeshCentral Router is a Windows tool for TCP port mapping. You can, for example, RDP into a remote device thru this server.</div><br />";
|
||
x += addHtmlValue('Win32 Executable', '<a style=cursor:pointer download href="meshagents?meshaction=winrouter" onclick="setDialogMode(0)">MeshCentralRouter.exe</a>');
|
||
setDialogMode(2, "MeshCentral Router", 1, null, x, "fileDownload");
|
||
}
|
||
|
||
// Show MeshCmd dialog
|
||
function p10showMeshCmdDialog(mode, nodeid) {
|
||
if (xxdialogMode) return;
|
||
var y = "<select id=aginsSelect onclick=meshCmdOsClick() style=width:236px>";
|
||
y += "<option value=3>Windows (32bit)</option>";
|
||
y += "<option value=4>Windows (64bit)</option>";
|
||
y += "<option value=5>Linux x86 (32bit)</option>";
|
||
y += "<option value=6>Linux x86 (64bit)</option>";
|
||
y += "<option value=16>MacOS (64bit)</option>";
|
||
y += "<option value=25>Linux ARM, Raspberry Pi (32bit)</option>";
|
||
y += "</select>";
|
||
|
||
var x = "";
|
||
if (mode == 0) { x += '<div>MeshCmd is a command line tool that performs lots of different operations. The action file can optionally be downloaded and edited to provide server information and credentials.<br /><br />'; }
|
||
if (mode == 1) { x += '<div>Download "meshcmd" with an action file to route traffic thru this server to this device. Make sure to edit meshaction.txt and add your account password or make any changes needed.<br /><br />'; }
|
||
x += addHtmlValue('Operating System', y);
|
||
x += addHtmlValue('MeshCmd', '<a id=meshcmddownloadid href="meshagents?meshcmd=3" download></a>');
|
||
if (mode == 0) { x += addHtmlValue('Action File', '<a href="meshagents?meshaction=generic" download>MeshAction (.txt)</a>'); }
|
||
if (mode == 1) { x += addHtmlValue('Action File', '<a href="meshagents?meshaction=route&nodeid=' + nodeid + '" download>MeshAction (.txt)</a>'); }
|
||
x += "</div>";
|
||
setDialogMode(2, ["Download MeshCmd","Network Router"][mode], 9, null, x, "fileDownload");
|
||
meshCmdOsClick();
|
||
}
|
||
|
||
function meshCmdOsClick() {
|
||
var os = Q('aginsSelect').value, osn = '';
|
||
//Q('meshcmddownloadid').href = "meshagents?meshcmd=" + os;
|
||
if (os == 3) { osn = 'MeshCmd (Win32 executable)'; }
|
||
if (os == 4) { osn = 'MeshCmd (Win64 executable)'; }
|
||
if (os == 5) { osn = 'MeshCmd (Linux x86, 32bit)'; }
|
||
if (os == 6) { osn = 'MeshCmd (Linux x86, 64bit)'; }
|
||
if (os == 16) { osn = 'MeshCmd (MacOS, 64bit)'; }
|
||
if (os == 25) { osn = 'MeshCmd (Linux ARM, 32bit)'; }
|
||
QH('meshcmddownloadid', osn);
|
||
}
|
||
|
||
function p10showiconselector() {
|
||
if (xxdialogMode) return;
|
||
var mesh = meshes[currentNode.meshid];
|
||
var meshrights = mesh.links[userinfo._id].rights;
|
||
if ((meshrights & 4) == 0) return;
|
||
|
||
var x = '<br><div style=display:inline-block;width:40px></div>';
|
||
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>';
|
||
x += '<div style=display:inline-block class=i5 onclick=p10setIcon(5)></div>';
|
||
x += '<div style=display:inline-block class=i6 onclick=p10setIcon(6)></div><br><br>';
|
||
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 = ['', '', '', 'Tag1, Tag2, Tag3'];
|
||
function showEditNodeValueDialog(mode) {
|
||
if (xxdialogMode) return;
|
||
var x = addHtmlValue(showEditNodeValueDialog_modes[mode], '<input id=dp10devicevalue 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 desktopNode;
|
||
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)) {
|
||
var xdesk = multiDesktop[currentNode._id];
|
||
if (xdesk != null) {
|
||
// This device already has a canvas, use it.
|
||
QH('DeskParent', '');
|
||
var c = xdesk.m.CanvasId;
|
||
c.setAttribute('id', 'Desk');
|
||
c.setAttribute('onmousedown', 'dmousedown(event)');
|
||
c.setAttribute('onmouseup', 'dmouseup(event)');
|
||
c.setAttribute('onmousemove', 'dmousemove(event)');
|
||
c.removeAttribute('onclick');
|
||
Q('DeskParent').appendChild(c);
|
||
desktop = xdesk;
|
||
if (desktop.m.SendCompressionLevel) { desktop.m.SendCompressionLevel(1, desktopsettings.quality, desktopsettings.scaling, desktopsettings.framerate); }
|
||
desktop.onStateChanged = onDesktopStateChange;
|
||
desktopNode = currentNode;
|
||
onDesktopStateChange(desktop, desktop.State);
|
||
delete multiDesktop[currentNode._id];
|
||
} else {
|
||
// Device is not already connected, just setup a blank canvas
|
||
QH('DeskParent', '<canvas id=Desk 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();
|
||
deskAdjust();
|
||
|
||
// 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 = mesh.links[userinfo._id].rights;
|
||
|
||
// Show the right buttons
|
||
QV('disconnectbutton1span', (deskState != 0));
|
||
QV('connectbutton1span', (deskState == 0) && ((meshrights & 8) || (meshrights & 256)) && (mesh.mtype == 2) && (currentNode.agent.caps & 1));
|
||
QV('connectbutton1hspan', (deskState == 0) && (meshrights & 8) && ((currentNode.intelamt != null) && (mesh.mtype == 1 || currentNode.intelamt.state == 2) && ((currentNode.intelamt.ver != null) || (mesh.mtype == 1))));
|
||
|
||
// Show the right settings
|
||
QV('d7amtkvm', (currentNode.intelamt != null && ((currentNode.intelamt.ver != null) || (mesh.mtype == 1))) && ((deskState == 0) || (desktop.contype == 2)));
|
||
QV('d7meshkvm', (webRtcDesktop) || ((mesh.mtype == 2) && (currentNode.agent.caps & 1) && ((deskState == false) || (desktop.contype == 1))));
|
||
|
||
// Enable buttons
|
||
var inputAllowed = (meshrights == 0xFFFFFFFF) || (((meshrights & 8) != 0) && ((meshrights & 256) == 0) && ((meshrights & 4096) == 0));
|
||
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('deskFocusBtn', (desktop != null) && (desktop.contype == 2) && (deskState != 0) && (desktopsettings.showfocus));
|
||
QV('DeskCAD', inputAllowed);
|
||
QE('DeskCAD', deskState == 3);
|
||
QV('DeskClip', (currentNode.agent) && (currentNode.agent.id != 11) && (currentNode.agent.id != 16) && ((desktop == null) || (desktop.contype != 2))); // Clipboard not supported on MacOS
|
||
QE('DeskClip', deskState == 3);
|
||
QV('DeskWD', (currentNode.agent) && (currentNode.agent.id < 5) && inputAllowed);
|
||
QE('DeskWD', deskState == 3);
|
||
QV('deskkeys', (currentNode.agent) && (currentNode.agent.id < 5) && inputAllowed);
|
||
QE('deskkeys', deskState == 3);
|
||
|
||
QV('DeskToolsButton', (inputAllowed) && (mesh.mtype == 2) && online);
|
||
QV('DeskChatButton', (browserfullscreen == false) && (inputAllowed) && (mesh.mtype == 2) && online);
|
||
QV('DeskNotifyButton', (browserfullscreen == false) && (currentNode.agent) && (currentNode.agent.id < 5) && (inputAllowed) && (mesh.mtype == 2) && online);
|
||
QV('DeskOpenWebButton', (browserfullscreen == false) && (inputAllowed) && (mesh.mtype == 2) && online);
|
||
|
||
QV('DeskControlSpan', inputAllowed)
|
||
QV('deskActionsBtn', (browserfullscreen == false));
|
||
QV('deskActionsSettings', (browserfullscreen == false));
|
||
if (meshrights & 8) { Q('DeskControl').checked = (getstore('DeskControl', 1) == 1); } else { Q('DeskControl').checked = false; }
|
||
if (online == false) QV('DeskTools', false);
|
||
}
|
||
|
||
// Debug
|
||
var autoConnectDesktopTimer = null;
|
||
function autoConnectDesktop(e) { if (autoConnectDesktopTimer == null) { autoConnectDesktopTimer = setInterval(connectDesktop, 100); } else { clearInterval(autoConnectDesktopTimer); autoConnectDesktopTimer = null; } }
|
||
|
||
function connectDesktop(e, contype) {
|
||
p11clearConsoleMsg();
|
||
if (desktop == null) {
|
||
desktopNode = currentNode;
|
||
if (contype == 2) {
|
||
// Setup the Intel AMT remote desktop
|
||
if ((desktopNode.intelamt.user == null) || (desktopNode.intelamt.user == '')) { editDeviceAmtSettings(desktopNode._id, connectDesktop, 2); 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.localKeyMap = desktopsettings.localkeymap;
|
||
desktop.m.showmouse = desktopsettings.showmouse;
|
||
desktop.m.onScreenSizeChange = deskAdjust;
|
||
desktop.m.onKvmData = function (x) {
|
||
//console.log('onKvmData (' + x.length + '): ' + x);
|
||
// Send the presense probe only once if needed.
|
||
if (x.length == 0) { if (!desktop.m._sentPresence) { desktop.m._sentPresence = true; desktop.m.sendKvmData(JSON.stringify({ action: 'present', ver: 1 })); } return; }
|
||
var data = null;
|
||
try { data = JSON.parse(x); } catch (e) { }
|
||
if ((data != null) && (data.action != null)) {
|
||
if (data.action == 'restart') {
|
||
// Clear WebRTC channel
|
||
webRtcDesktopReset();
|
||
desktop.m.sendKvmData(JSON.stringify({ action: 'present', ver: 1 }));
|
||
} else if ((data.action == 'present') && (webRtcDesktop == null)) {
|
||
// Setup WebRTC channel
|
||
webRtcDesktop = { platform: data.platform };
|
||
var configuration = null; //{ "iceServers": [ { 'urls': 'stun:stun.services.mozilla.com' }, { 'urls': 'stun:stun.l.google.com:19302' } ] };
|
||
if (typeof RTCPeerConnection !== 'undefined') { webRtcDesktop.webrtc = new RTCPeerConnection(configuration); }
|
||
else if (typeof webkitRTCPeerConnection !== 'undefined') { webRtcDesktop.webrtc = new webkitRTCPeerConnection(configuration); }
|
||
|
||
webRtcDesktop.webchannel = webRtcDesktop.webrtc.createDataChannel("DataChannel", {}); // { ordered: false, maxRetransmits: 2 }
|
||
webRtcDesktop.webchannel.onopen = function () {
|
||
// Switch to software KVM
|
||
//if (urlvars && urlvars['kvmdatatrace']) { console.log('WebRTC Data Channel Open'); }
|
||
console.log('WebRTC Data Channel Open');
|
||
Q('deskstatus').textContent = StatusStrs[desktop.State] + ', Soft-KVM';
|
||
desktop.m.hold(true);
|
||
webRtcDesktop.webRtcActive = true;
|
||
webRtcDesktop.softdesktop = CreateKvmDataChannel(webRtcDesktop.webchannel, CreateAgentRemoteDesktop('Desk', Q('id_mainarea')), desktop.m);
|
||
webRtcDesktop.softdesktop.m.setRotation(desktop.m.rotation);
|
||
webRtcDesktop.softdesktop.m.onScreenSizeChange = deskAdjust;
|
||
if (desktopsettings.quality) { webRtcDesktop.softdesktop.m.CompressionLevel = desktopsettings.quality; } // Number from 1 to 100. 50 or less is best.
|
||
if (desktopsettings.scaling) { webRtcDesktop.softdesktop.m.ScalingLevel = desktopsettings.scaling; }
|
||
webRtcDesktop.softdesktop.Start();
|
||
|
||
// Check if we can get remote file access
|
||
// ###BEGIN###{DesktopInbandFiles}
|
||
/*
|
||
QV('go24', true); // Files
|
||
downloadFile = null;
|
||
p24files = webRtcDesktop.softdesktop;
|
||
p24targetpath = '';
|
||
webRtcDesktop.softdesktop.onControlMsg = onFilesControlData;
|
||
webRtcDesktop.softdesktop.sendCtrlMsg(JSON.stringify({ action: 'ls', reqid: 1, path: '' })); // Ask for the root folder
|
||
*/
|
||
// ###END###{DesktopInbandFiles}
|
||
}
|
||
webRtcDesktop.webchannel.onclose = function (event) {
|
||
//if (urlvars['kvmdatatrace']) { console.log('WebRTC Data Channel Closed'); }
|
||
console.log('WebRTC Data Channel Closed');
|
||
webRtcDesktopReset();
|
||
}
|
||
webRtcDesktop.webrtc.onicecandidate = function (e) {
|
||
if (e.candidate == null) {
|
||
desktop.m.sendKvmData(JSON.stringify({ action: 'offer', ver: 1, sdp: webRtcDesktop.webrtcoffer.sdp }));
|
||
} else {
|
||
webRtcDesktop.webrtcoffer.sdp += ("a=" + e.candidate.candidate + "\r\n"); // New candidate, add it to the SDP
|
||
}
|
||
}
|
||
webRtcDesktop.webrtc.oniceconnectionstatechange = function () {
|
||
if ((webRtcDesktop != null) && (webRtcDesktop.webrtc != null) && ((webRtcDesktop.webrtc.iceConnectionState == 'disconnected') || (webRtcDesktop.webrtc.iceConnectionState == 'failed'))) { /*console.log('WebRTC ICE Failed');*/ webRtcDesktopReset(); }
|
||
}
|
||
webRtcDesktop.webrtc.createOffer(function (offer) {
|
||
// Got the offer
|
||
webRtcDesktop.webrtcoffer = offer;
|
||
webRtcDesktop.webrtc.setLocalDescription(offer, function () { }, webRtcDesktopReset);
|
||
}, webRtcDesktopReset, { mandatory: { OfferToReceiveAudio: false, OfferToReceiveVideo: false } });
|
||
} else if ((data.action == 'answer') && (webRtcDesktop != null)) {
|
||
// Complete the WebRTC channel
|
||
webRtcDesktop.webrtc.setRemoteDescription(new RTCSessionDescription({ type: 'answer', sdp: data.sdp }), function () { }, webRtcDesktopReset);
|
||
}
|
||
}
|
||
};
|
||
desktop.Start(desktopNode._id, 16994, '*', '*', 0);
|
||
desktop.contype = 2;
|
||
} else {
|
||
// Setup the Mesh Agent remote desktop
|
||
desktop = CreateAgentRedirect(meshserver, CreateAgentRemoteDesktop('Desk'), serverPublicNamePort, authCookie, domainUrl);
|
||
desktop.debugmode = debugmode;
|
||
desktop.m.debugmode = debugmode;
|
||
desktop.attemptWebRTC = attemptWebRTC;
|
||
desktop.onStateChanged = onDesktopStateChange;
|
||
desktop.onConsoleMessageChange = function () {
|
||
p11clearConsoleMsg();
|
||
if (desktop.consoleMessage) {
|
||
QH('p11DeskConsoleMsg', EscapeHtml(desktop.consoleMessage).split('\n').join('<br />'));
|
||
QV('p11DeskConsoleMsg', true);
|
||
p11DeskConsoleMsgTimer = setTimeout(p11clearConsoleMsg, 8000);
|
||
}
|
||
}
|
||
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();
|
||
webRtcDesktopReset();
|
||
desktopNode = desktop = null;
|
||
}
|
||
}
|
||
|
||
function p11clearConsoleMsg() { QV('p11DeskConsoleMsg', false); if (p11DeskConsoleMsgTimer) { clearTimeout(p11DeskConsoleMsgTimer); p11DeskConsoleMsgTimer = null; } }
|
||
function p12clearConsoleMsg() { QV('p12TermConsoleMsg', false); if (p12TermConsoleMsgTimer) { clearTimeout(p12TermConsoleMsgTimer); p12TermConsoleMsgTimer = null; } }
|
||
function p13clearConsoleMsg() { QV('p13FilesConsoleMsg', false); if (p13FilesConsoleMsgTimer) { clearTimeout(p13FilesConsoleMsgTimer); p13FilesConsoleMsgTimer = null; } }
|
||
|
||
var webRtcDesktop = null;
|
||
function webRtcDesktopReset() {
|
||
if (webRtcDesktop == null) return;
|
||
if (webRtcDesktop.softdesktop != null) { webRtcDesktop.softdesktop.Stop(); webRtcDesktop.softdesktop = null; }
|
||
if (webRtcDesktop.webchannel != null) { try { webRtcDesktop.webchannel.close(); } catch (e) { } webRtcDesktop.webchannel = null; }
|
||
if (webRtcDesktop.webrtc != null) { try { webRtcDesktop.webrtc.close(); } catch (e) { } webRtcDesktop.webrtc = null; }
|
||
webRtcDesktop = null;
|
||
// Switch back to hardware KVM
|
||
if (desktop && desktop.m) {
|
||
desktop.m.hold(false);
|
||
Q('deskstatus').textContent = StatusStrs[desktop.State];
|
||
}
|
||
// ###BEGIN###{DesktopInbandFiles}
|
||
/*
|
||
p24files = null;
|
||
p24downloadFileCancel() // If any downloads are in process, cancel them.
|
||
p24uploadFileCancel(); // If any uploads are in process, cancel them.
|
||
QV('go24', false); // Files
|
||
if (currentView == 24) { go(14); }
|
||
*/
|
||
// ###END###{DesktopInbandFiles}
|
||
}
|
||
|
||
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('DeskFocus', false);
|
||
QV('termdisplays', false);
|
||
deskFocusBtn.value = 'All Focus';
|
||
if (fullscreen == true) { deskToggleFull(); }
|
||
webRtcDesktopReset();
|
||
deskPreferedStickyDisplay = 0;
|
||
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;
|
||
desktopsettings.localkeymap = d7localKeyMap.checked;
|
||
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 (desktopsettings.showfocus == false) { desktop.m.focusmode = 0; deskFocusBtn.value = 'All Focus'; }
|
||
if (desktop.State != 0) { desktop.Stop(); setTimeout(function () { connectDesktop(null, 2); }, 50); }
|
||
}
|
||
}
|
||
}
|
||
|
||
function applyDesktopSettings() {
|
||
var r = '', ops = (features & 512)?[90,80,70,60,50,40,30,20,10,5,1]:[60,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; }
|
||
if (desktopsettings.localkeymap) { d7localKeyMap.checked = desktopsettings.localkeymap; }
|
||
QV('deskFocusBtn', (desktop != null) && (desktop.contype == 2) && (desktop.state != 0) && (desktopsettings.showfocus));
|
||
}
|
||
|
||
// Enter browser fullscreen
|
||
function enterBrowserFullscreen(elem) {
|
||
if (elem.requestFullscreen) { elem.requestFullscreen(); }
|
||
else if (elem.msRequestFullscreen) { elem.msRequestFullscreen(); }
|
||
else if (elem.mozRequestFullScreen) { elem.mozRequestFullScreen(); }
|
||
else if (elem.webkitRequestFullscreen) { elem.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); }
|
||
}
|
||
|
||
// Exit browser fullscreen
|
||
function exitBrowserFullscreen() {
|
||
if (document.exitFullscreen) { document.exitFullscreen(); }
|
||
else if (document.msExitFullscreen) { document.msExitFullscreen(); }
|
||
else if (document.mozCancelFullScreen) { document.mozCancelFullScreen(); }
|
||
else if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); }
|
||
}
|
||
|
||
// Return true if the browser is fullscreen. This is a delayed method that will return true/false late. Not very useful.
|
||
function isBrowserFullscreen() {
|
||
if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement) { return false; } else { return true; }
|
||
}
|
||
|
||
var fullscreen = false;
|
||
var browserfullscreen = false;
|
||
function deskToggleFull(e) {
|
||
fullscreen = !fullscreen;
|
||
if (fullscreen) {
|
||
QC('body').add("fulldesk");
|
||
//QS('deskarea3x').height = null;
|
||
// If shift is pressed, enter browser full screen.
|
||
if (e.shiftKey == true) { enterBrowserFullscreen(Q('deskarea0')); browserfullscreen = true; }
|
||
} else {
|
||
QC('body').remove("fulldesk");
|
||
exitBrowserFullscreen();
|
||
browserfullscreen = false;
|
||
toggleFullScreen();
|
||
}
|
||
deskAdjust();
|
||
//deskAdjust();
|
||
updateDesktopButtons();
|
||
}
|
||
|
||
function deskToggleFocus() {
|
||
desktop.m.focusmode = (desktop.m.focusmode + 64) % 192;
|
||
Q('deskFocusBtn').value = ['All Focus', 'Small Focus', 'Large Focus'][desktop.m.focusmode / 64];
|
||
}
|
||
|
||
function deskAdjust() {
|
||
var parentH = Q('DeskParent').clientHeight, parentW = Q('DeskParent').clientWidth;
|
||
var deskH = Q('Desk').height, deskW = Q('Desk').width;
|
||
|
||
if (deskAspectRatio == 2) {
|
||
// Scale mode
|
||
QS('Desk')['margin-top'] = null;
|
||
QS('Desk').height = '100%';
|
||
QS('Desk').width = '100%';
|
||
//QS('deskarea3x').height = null;
|
||
QS('DeskParent').overflow = 'hidden';
|
||
} else if (deskAspectRatio == 1) {
|
||
// Zoomed mode
|
||
QS('Desk')['margin-top'] = '0px';
|
||
QS('Desk').height = deskH + 'px';
|
||
QS('Desk').width = deskW + 'px';
|
||
QS('DeskParent').overflow = 'scroll';
|
||
} else {
|
||
// Fixed aspect ratio
|
||
if ((parentH / parentW) > (deskH / deskW)) {
|
||
var hNew = ((deskH * parentW) / deskW) + 'px';
|
||
//if (webPageFullScreen || fullscreen) {
|
||
//QS('deskarea3x').height = null;
|
||
//} else {
|
||
// QS('deskarea3x').height = hNew;
|
||
//QS('deskarea3x').height = null;
|
||
//}
|
||
QS('Desk').height = hNew;
|
||
QS('Desk').width = '100%';
|
||
} else {
|
||
var wNew = ((deskW * parentH) / deskH) + 'px';
|
||
if (webPageFullScreen || fullscreen) {
|
||
QS('Desk').height = null;
|
||
} else {
|
||
QS('Desk').height = '100%';
|
||
}
|
||
QS('Desk').width = wNew;
|
||
}
|
||
QS('Desk')['margin-top'] = null;
|
||
QS('DeskParent').overflow = 'hidden';
|
||
}
|
||
}
|
||
|
||
function mdeskAdjust(mod, sw, sh, cv) {
|
||
if (!mod || !sw || !sh || !cv) return;
|
||
|
||
// Check if we are in single desktop mode
|
||
if (cv.id == "Desk") { deskAdjust(); return; }
|
||
|
||
// Figure out and adjust the size to fill the width of the div
|
||
var vsize = [{ x: 180, y: 101 }, { x: 302, y: 169 }, { x: 454, y: 255 }][Q('sizeselect').selectedIndex];
|
||
var realw = vsize.x + 2, tw = Q('xdevices').clientWidth - 30, xw = Math.floor(tw / realw);
|
||
xw = realw + Math.floor((tw - (xw * realw)) / xw);
|
||
vsize.y = vsize.y * (xw / vsize.x);
|
||
vsize.x = xw;
|
||
var mh = vsize.y, mw = vsize.x;
|
||
if (mod.State != 0) { mh = vsize.y; mw = (sw / sh) * vsize.y; }
|
||
QS(cv.id)['max-height'] = mh + 'px';
|
||
QS(cv.id)['max-width'] = mw + 'px';
|
||
QS(cv.id)['margin-top'] = '0';
|
||
QS(cv.id)['margin-bottom'] = '0';
|
||
}
|
||
|
||
// 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
|
||
}
|
||
}
|
||
}
|
||
|
||
// Show clipboard dialog
|
||
function showDeskClip() {
|
||
if (xxdialogMode || desktop == null || desktop.State != 3) return;
|
||
Q('DeskClip').blur();
|
||
var x = '';
|
||
x += '<input id=dlgClipGet type=button value="Get Clipboard" style=width:120px onclick=showDeskClipGet()>';
|
||
x += '<input id=dlgClipSet type=button value="Set Clipboard" style=width:120px onclick=showDeskClipSet()>';
|
||
x += '<div id=dlgClipStatus style="display:inline-block;margin-left:8px" ></div>';
|
||
x += '<textarea id=d2clipText style="width:100%;height:184px;resize:none" maxlength=65535></textarea>';
|
||
x += '<input type=button value="Close" style=width:80px;float:right onclick=dialogclose(0)><div style=height:26px;margin-top:3px><span id=linuxClipWarn style=display:none>Remote clipboard is valid for 60 seconds.</span> </div><div></div>';
|
||
setDialogMode(2, "Remote Clipboard", 8, null, x, 'clipboard');
|
||
Q('d2clipText').focus();
|
||
}
|
||
|
||
function showDeskClipGet() {
|
||
if (desktop == null || desktop.State != 3) return;
|
||
meshserver.send({ action: 'msg', type: 'getclip', nodeid: currentNode._id });
|
||
}
|
||
|
||
function showDeskClipSet() {
|
||
if (desktop == null || desktop.State != 3) return;
|
||
meshserver.send({ action: 'msg', type: 'setclip', nodeid: currentNode._id, data: Q('d2clipText').value });
|
||
QV('linuxClipWarn', currentNode && currentNode.agent && (currentNode.agent.id > 4) && (currentNode.agent.id != 21) && (currentNode.agent.id != 22));
|
||
}
|
||
|
||
// Send CTRL-ALT-DEL
|
||
function sendCAD() {
|
||
if (xxdialogMode || desktop == null || desktop.State != 3) return;
|
||
desktop.m.sendcad();
|
||
}
|
||
|
||
// Show process dialogs
|
||
function toggleDeskTools() {
|
||
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() {
|
||
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 title="Stop process" 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);
|
||
}
|
||
}
|
||
|
||
// Toggle mouse and keyboard input
|
||
function toggleKvmControl() { putstore('DeskControl', (Q("DeskControl").checked?1:0)); }
|
||
|
||
// Save the desktop image to file
|
||
function deskSaveImage() {
|
||
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 + ".jpg"); });
|
||
}
|
||
|
||
function deskDisplayInfo(sender, displays, selDisplay) {
|
||
var displayCount = 0, displaySelector = '';
|
||
for (var i in displays) {
|
||
displayCount++;
|
||
displaySelector += '<option' + ((selDisplay == i) ? ' selected' : '') + ' value=' + i + '>' + displays[i] + '</option>';
|
||
if ((deskPreferedStickyDisplay == i) && (selDisplay != deskPreferedStickyDisplay)) { desktop.m.SetDisplay(i); }
|
||
}
|
||
QH('termdisplays', displaySelector);
|
||
QV('termdisplays', displayCount > 1);
|
||
}
|
||
|
||
function deskGetDisplayNumbers(e) { desktop.m.GetDisplayNumbers(); }
|
||
var deskPreferedStickyDisplay = 0;
|
||
function deskSetDisplay(e) { desktop.m.SetDisplay(deskPreferedStickyDisplay = parseInt(Q('termdisplays').value)); }
|
||
|
||
// Double click detection. This is important for MacOS.
|
||
var dblClickDetectArgs = { t:0, x:0, y:0 };
|
||
function dblClickDetect(e) {
|
||
if (e.buttons != 1) return;
|
||
var t = Date.now();
|
||
if (((t - dblClickDetectArgs.t) < 250) && (Math.abs(e.clientX - dblClickDetectArgs.x) < 2) && (Math.abs(e.clientY - dblClickDetectArgs.y) < 2)) {
|
||
if (!xxdialogMode && desktop != null && Q('DeskControl').checked) { if ((webRtcDesktop != null) && (webRtcDesktop.softdesktop != null)) { webRtcDesktop.softdesktop.m.mousedblclick(e); desktop.m.sendKeepAlive(); } else { desktop.m.mousedblclick(e); } }
|
||
}
|
||
dblClickDetectArgs.t = t;
|
||
dblClickDetectArgs.x = e.clientX;
|
||
dblClickDetectArgs.y = e.clientY;
|
||
}
|
||
|
||
function dmousedown(e) { setSessionActivity(); e.addx = Q('DeskParent').scrollLeft; e.addy = Q('DeskParent').scrollTop; if (!xxdialogMode && desktop != null && Q('DeskControl').checked) { if ((webRtcDesktop != null) && (webRtcDesktop.softdesktop != null)) { webRtcDesktop.softdesktop.m.mousedown(e); desktop.m.sendKeepAlive(); } else { desktop.m.mousedown(e); } } dblClickDetect(e); }
|
||
function dmouseup(e) { setSessionActivity(); e.addx = Q('DeskParent').scrollLeft; e.addy = Q('DeskParent').scrollTop; if (!xxdialogMode && desktop != null && Q('DeskControl').checked) if ((webRtcDesktop != null) && (webRtcDesktop.softdesktop != null)) { webRtcDesktop.softdesktop.m.mouseup(e); desktop.m.sendKeepAlive(); } else { desktop.m.mouseup(e); } }
|
||
function dmousemove(e) { setSessionActivity(); e.addx = Q('DeskParent').scrollLeft; e.addy = Q('DeskParent').scrollTop; if (!xxdialogMode && desktop != null && Q('DeskControl').checked) { if ((webRtcDesktop != null) && (webRtcDesktop.softdesktop != null)) { webRtcDesktop.softdesktop.m.mousemove(e); desktop.m.sendKeepAlive(); } else { desktop.m.mousemove(e); } } }
|
||
function dmousewheel(e) { setSessionActivity(); e.addx = Q('DeskParent').scrollLeft; e.addy = Q('DeskParent').scrollTop; if (!xxdialogMode && desktop != null && Q('DeskControl').checked) { if ((webRtcDesktop != null) && (webRtcDesktop.softdesktop != null)) { webRtcDesktop.softdesktop.m.mousewheel(e); desktop.m.sendKeepAlive(); } else { if (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, 'Stop process #' + id + ' "' + name + '"?', id); }
|
||
function stopProcessEx(buttons, tag) { meshserver.send({ action: 'msg', type:'pskill', nodeid: currentNode._id, value: tag }); setTimeout(refreshDeskTools, 300); }
|
||
|
||
//
|
||
// TERMINAL
|
||
//
|
||
|
||
var terminalNode;
|
||
function setupTerminal() {
|
||
// Setup the terminal
|
||
if ((terminalNode != currentNode) && (terminal != null)) { terminal.Stop(); terminal = null; }
|
||
terminalNode = currentNode;
|
||
updateTerminalButtons();
|
||
}
|
||
|
||
// Show and enable the right buttons
|
||
function updateTerminalButtons() {
|
||
var mesh = meshes[terminalNode.meshid];
|
||
var termState = ((terminal != null) && (terminal.state != 0));
|
||
|
||
// Show the right buttons
|
||
QV('disconnectbutton2span', (termState == true));
|
||
QV('connectbutton2span', (termState == false) && (mesh.mtype == 2) && (currentNode.agent.caps & 2));
|
||
QV('connectbutton2hspan', (termState == false) && ((terminalNode.intelamt != null) && (mesh.mtype == 1 || terminalNode.intelamt.state == 2) && ((terminalNode.intelamt.ver != null) || (mesh.mtype == 1))));
|
||
|
||
// Enable buttons
|
||
var online = ((terminalNode.conn & 1) != 0); // If Agent (1) connected, enable Terminal
|
||
QE('connectbutton2', online);
|
||
var hwonline = ((terminalNode.conn & 6) != 0); // If CIRA (2) or AMT (4) connected, enable hardware terminal
|
||
QE('connectbutton2h', hwonline);
|
||
|
||
// Key buttons
|
||
QE('ctrlcbutton', termState);
|
||
QE('ctrlxbutton', termState);
|
||
QE('escbutton', termState);
|
||
QE('bsbutton', termState);
|
||
QE('pastebutton', termState);
|
||
QE('specialkeylist', termState);
|
||
QE('specialkeylistinput', termState);
|
||
|
||
// Terminal settings
|
||
QV('terminalSettingsButtons', (terminal) && (terminal.contype == 2));
|
||
if (terminal) {
|
||
Q('id_ttypebutton').value = terminalEmulations[terminal.m.terminalEmulation];
|
||
Q('id_tfxkeysbutton').value = fxEmulations[terminal.m.fxEmulation];
|
||
Q('id_tcrbutton').value = (terminal.m.lineFeed == '\r\n')?'CR+LF':'LF';
|
||
}
|
||
}
|
||
|
||
// Called when the terminal state changes
|
||
function onTerminalStateChange(xterminal, state) {
|
||
var xstate = state;
|
||
if ((xstate == 3) && (xterminal.contype == 2)) { xstate++; }
|
||
var str = StatusStrs[xstate];
|
||
if (terminal.webRtcActive == true) { str += ', WebRTC'; }
|
||
QH('termstatus', str);
|
||
switch (state) {
|
||
case 0:
|
||
// Disconnected, clear the terminal
|
||
QE('termSizeList', true);
|
||
QH('termtitle', '');
|
||
xterminal.m.TermResetScreen();
|
||
xterminal.m.TermDraw();
|
||
if (terminal != null) { terminal.Stop(); terminal = null; }
|
||
break;
|
||
case 3:
|
||
QE('termSizeList', false);
|
||
break;
|
||
default:
|
||
QE('termSizeList', false);
|
||
//console.log('Unhandled onTerminalStateChange state', state);
|
||
break;
|
||
}
|
||
updateTerminalButtons();
|
||
}
|
||
|
||
// DEBUG
|
||
var autoConnectTerminalTimer = null;
|
||
function autoConnectTerminal(e) { if (autoConnectTerminalTimer == null) { autoConnectTerminalTimer = setInterval(connectTerminal, 100); } else { clearInterval(autoConnectTerminalTimer); autoConnectTerminalTimer = null; } }
|
||
|
||
function connectTerminal(e, contype) {
|
||
p12clearConsoleMsg();
|
||
if (!terminal) {
|
||
if (contype == 2) {
|
||
// Setup the Intel AMT terminal
|
||
if ((terminalNode.intelamt.user == null) || (terminalNode.intelamt.user == '')) { editDeviceAmtSettings(terminalNode._id, connectTerminal, 2); return; }
|
||
var termoptions = {};
|
||
if (Q('termSizeList').value == 2) { termoptions.width = 100; termoptions.height = 30; }
|
||
terminal = CreateAmtRedirect(CreateAmtRemoteTerminal('Term', termoptions), authCookie);
|
||
terminal.debugmode = debugmode;
|
||
terminal.m.debugmode = debugmode;
|
||
terminal.m.onTitleChange = function (sender, title) { QH('termtitle', ' - ' + EscapeHtml(title)); }
|
||
terminal.onStateChanged = onTerminalStateChange;
|
||
terminal.Start(terminalNode._id, 16994, '*', '*', 0);
|
||
terminal.contype = 2;
|
||
Q('id_ttypebutton').value = terminalEmulations[terminal.m.terminalEmulation];
|
||
} else {
|
||
// Setup a mesh agent terminal
|
||
var termoptions = {};
|
||
if ([1, 2, 3, 4, 21, 22].indexOf(currentNode.agent.id) == -1) {
|
||
if (Q('termSizeList').value == 2) { termoptions.width = 100; termoptions.height = 30; termoptions.xterm = true; }
|
||
if (Q('termSizeList').value == 3) {
|
||
// TODO: Try to improve terminal auto-size.
|
||
termoptions.width = Math.floor((Q('column_l').clientWidth - 60) / 10);
|
||
termoptions.height = Math.floor((Q('column_l').clientHeight - 120) / 20);
|
||
termoptions.xterm = true;
|
||
}
|
||
}
|
||
terminal = CreateAgentRedirect(meshserver, CreateAmtRemoteTerminal('Term', termoptions), serverPublicNamePort, authCookie, domainUrl);
|
||
terminal.debugmode = debugmode;
|
||
terminal.m.debugmode = debugmode;
|
||
terminal.m.onTitleChange = function (sender, title) { QH('termtitle', ' - ' + EscapeHtml(title)); }
|
||
terminal.m.lineFeed = ([1, 2, 3, 4, 21, 22].indexOf(currentNode.agent.id) >= 0) ? '\r\n' : '\r'; // On windows, send \r\n, on Linux only \r
|
||
terminal.attemptWebRTC = attemptWebRTC;
|
||
terminal.onStateChanged = onTerminalStateChange;
|
||
terminal.onConsoleMessageChange = function () {
|
||
p12clearConsoleMsg();
|
||
if (terminal.consoleMessage) {
|
||
QH('p12TermConsoleMsg', EscapeHtml(terminal.consoleMessage).split('\n').join('<br />'));
|
||
QV('p12TermConsoleMsg', true);
|
||
p12TermConsoleMsgTimer = setTimeout(p12clearConsoleMsg, 8000);
|
||
}
|
||
}
|
||
terminal.Start(terminalNode._id);
|
||
terminal.contype = 1;
|
||
terminal.m.terminalEmulation = 0;
|
||
terminal.m.fxEmulation = 0;
|
||
Q('id_ttypebutton').value = terminalEmulations[0];
|
||
}
|
||
} else {
|
||
//QH('Term', '');
|
||
terminal.Stop();
|
||
terminal = null;
|
||
}
|
||
Q('connectbutton2').blur(); // Deselect the connect button so the button does not get key presses.
|
||
}
|
||
|
||
var terminalEmulations = ['UTF8 Terminal', 'Extended ASCII', 'Intel ASCII'];
|
||
function termToggleType() {
|
||
if (!terminal || xxdialogMode) return;
|
||
terminal.m.terminalEmulation = (terminal.m.terminalEmulation + 1) % 3;
|
||
Q('id_ttypebutton').value = terminalEmulations[terminal.m.terminalEmulation];
|
||
Q('id_ttypebutton').blur(); // Deselect the connect button so the button does not get key presses.
|
||
}
|
||
|
||
var fxEmulations = ['Intel (F10 = ESC+[OM)', 'Alternate (F10 = ESC+0)', 'VT100+ (F10 = ESC+[OY)'];
|
||
function termToggleFx() {
|
||
if (!terminal || xxdialogMode) return;
|
||
terminal.m.fxEmulation = (terminal.m.fxEmulation + 1) % 3;
|
||
Q('id_tfxkeysbutton').value = fxEmulations[terminal.m.fxEmulation];
|
||
Q('id_tfxkeysbutton').blur(); // Deselect the connect button so the button does not get key presses.
|
||
}
|
||
|
||
function termToggleCr() {
|
||
if (!terminal || xxdialogMode) return;
|
||
if (terminal.m.lineFeed == '\n') { terminal.m.lineFeed = '\r\n'; } else { terminal.m.lineFeed = '\n'; }
|
||
Q('id_tcrbutton').value = (terminal.m.lineFeed == '\r\n') ? 'CR+LF' : 'LF';
|
||
}
|
||
|
||
function termSendKey(key, id) {
|
||
if (!terminal || xxdialogMode) return;
|
||
terminal.m.TermSendKey(key);
|
||
Q(id).blur(); // Deselect the connect button so the button does not get key presses.
|
||
}
|
||
|
||
function showTermPasteDialog() {
|
||
if (!terminal || xxdialogMode) return;
|
||
Q('pastebutton').blur();
|
||
setDialogMode(2, "Paste", 3, showTermPasteDialogEx, '<textarea id=d2pasteText style="width:100%;height:184px;resize:none"></textarea>');
|
||
Q('d2pasteText').focus();
|
||
}
|
||
|
||
function showTermPasteDialogEx() {
|
||
if (!terminal) return;
|
||
terminal.m.TermSendKeys(Q('d2pasteText').value);
|
||
}
|
||
|
||
// Send special key
|
||
function sendSpecialKey() {
|
||
terminal.m.TermSendKey(Q('specialkeylist').value);
|
||
Q('specialkeylist').blur();
|
||
Q('specialkeylistinput').blur();
|
||
}
|
||
|
||
//
|
||
// 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) {
|
||
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) {
|
||
p13clearConsoleMsg();
|
||
if (!files) {
|
||
// Setup a mesh agent files
|
||
files = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotFiles), serverPublicNamePort, authCookie, domainUrl);
|
||
files.attemptWebRTC = attemptWebRTC;
|
||
files.onStateChanged = onFilesStateChange;
|
||
files.onConsoleMessageChange = function () {
|
||
p13clearConsoleMsg();
|
||
if (files.consoleMessage) {
|
||
QH('p13FilesConsoleMsg', EscapeHtml(files.consoleMessage).split('\n').join('<br />'));
|
||
QV('p13FilesConsoleMsg', true);
|
||
p13FilesConsoleMsgTimer = setTimeout(p13clearConsoleMsg, 8000);
|
||
}
|
||
}
|
||
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; }
|
||
//console.log('p13gotFiles', data);
|
||
data = JSON.parse(decode_utf8(data));
|
||
if (data.action == 'download') { p13gotDownloadCommand(data); return; }
|
||
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) + ')>' + 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 = '<span title="' + EscapeHtml(name) + '">' + EscapeHtml(name.substring(0, 70)) + "...</span>"; } else { shortname = EscapeHtml(name); }
|
||
name = EscapeHtml(name);
|
||
|
||
// Figure out the date
|
||
var fdatestr = '';
|
||
if (f.d != null) { var fdate = new Date(f.d), fdatestr = printDateTime(fdate) + " "; }
|
||
|
||
// Figure out the size
|
||
var fsize = '';
|
||
if (f.s != null) { fsize = getFileSizeStr(f.s); }
|
||
|
||
var h = '';
|
||
if (f.t < 3) {
|
||
var right = '', title = '';
|
||
h = "<div class=filelist file=999><input file=999 style=float:left name=fd class=fcb type=checkbox onchange=p13setActions() value='" + f.nx + "'> <span style=float:right title=\"" + title + "\">" + right + "</span><span><div class=fileIcon" + f.t + " onclick=p13folderset(\"" + encodeURIComponent(f.nx) + "\")></div><a style=cursor:pointer onclick=p13folderset(\"" + encodeURIComponent(f.nx) + "\")>" + shortname + "</a></span></div>";
|
||
} else {
|
||
var link = shortname;
|
||
if (f.s > 0) { link = "<a rel=\"noreferrer noopener\" target=\"_blank\" style=cursor:pointer onclick=\"p13downloadfile('" + encodeURIComponent(newlinkpath + '/' + name) + "','" + encodeURIComponent(name) + "'," + f.s + ")\">" + shortname + "</a>"; }
|
||
h = "<div class=filelist file=3><input file=3 style=float:left name=fd class=fcb type=checkbox onchange=p13setActions() value='" + f.nx + "'> <span class=fsize>" + fdatestr + "</span><span style=float:right>" + 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 = 'Select 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 ? 'Select None' : 'Select 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 /><input type=checkbox id=p13recdeleteinput>Recursive delete<br>" : "<input type=checkbox id=p13recdeleteinput style='display:none'>"; setDialogMode(2, "Delete", 3, p13deletefileEx, (cc > 1) ? ('Delete ' + cc + ' selected items?' + 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); }
|
||
|
||
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)) { x = 'Confim ' + (p13clipboardCut == 0?'copy':'move') + ' of ' + p13clipboard.length + ' entrie' + ((p13clipboard.length > 1)?'s':'') + ' 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)) { x = 'Holding ' + p13clipboard.length + ' entrie' + ((p13clipboard.length > 1)?'s':'') + ' for ' + (p13clipboardCut == 0?'copy':'move') + ', <a onclick=p13clearClip() style=cursor:pointer>Clear</a>.' } QH('p13bottomstatus', x); p13setActions(); }
|
||
function p13clearClip() { p13clipboard = null; p13clipboardFolder = null; p13clipboardCut = 0; p13updateClipview(); }
|
||
|
||
function p13fileDragDrop(e) {
|
||
haltEvent(e);
|
||
QV('p13bigfail', false);
|
||
QV('p13bigok', false);
|
||
if (e.dataTransfer == null || e.dataTransfer.files.length == 0 || p13filetree == null) return;
|
||
p13doUploadFiles(e.dataTransfer.files);
|
||
}
|
||
|
||
var p13dragtimer = null;
|
||
function p13fileDragOver(e) {
|
||
haltEvent(e);
|
||
if (p13dragtimer != null) { clearTimeout(p13dragtimer); p13dragtimer = null; }
|
||
var ac = (p13filetree != null); // Set to true if we can accept the file
|
||
QV('p13bigok', ac);
|
||
QV('p13bigfail', !ac);
|
||
}
|
||
|
||
function p13fileDragLeave(e) {
|
||
haltEvent(e);
|
||
if (e.target.id != "p13filetable") {
|
||
QV('p13bigfail', false);
|
||
QV('p13bigok', false);
|
||
} else {
|
||
p13dragtimer = setTimeout(function () { QV('p13bigfail',false); QV('p13bigok',false); p13dragtimer=null; }, 10);
|
||
}
|
||
}
|
||
|
||
//
|
||
// 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, 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;
|
||
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 />');
|
||
p13uploadReconnect();
|
||
}
|
||
|
||
function onFileUploadStateChange(xdownloadFile, state) {
|
||
switch (state) {
|
||
case 0:
|
||
p13folderup(9999);
|
||
break;
|
||
case 3:
|
||
p13uploadNextFile();
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Connect again
|
||
function p13uploadReconnect() {
|
||
uploadFile.ws = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotUploadData), serverPublicNamePort, authCookie, domainUrl);
|
||
uploadFile.ws.attemptWebRTC = false;
|
||
uploadFile.ws.ctrlMsgAllowed = false;
|
||
uploadFile.ws.onStateChanged = onFileUploadStateChange;
|
||
uploadFile.ws.Start(filesNode._id);
|
||
}
|
||
|
||
// 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;
|
||
|
||
uploadFile.xreader = new FileReader();
|
||
uploadFile.xreader.onload = function() {
|
||
uploadFile.xdata = uploadFile.xreader.result;
|
||
uploadFile.ws.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xdata.byteLength }));
|
||
};
|
||
uploadFile.xreader.readAsArrayBuffer(file);
|
||
} else {
|
||
p13uploadFileCancel();
|
||
}
|
||
}
|
||
|
||
// Used to cancel the entire transfer.
|
||
function p13uploadFileCancel(button, tag) {
|
||
if (uploadFile != null) {
|
||
if (uploadFile.ws != null) {
|
||
uploadFile.ws.Stop();
|
||
uploadFile.ws = null;
|
||
}
|
||
uploadFile = null;
|
||
}
|
||
setDialogMode(0); // Close any dialog boxes if present
|
||
}
|
||
|
||
// Receive upload ack from the mesh agent, use this to keep sending more data
|
||
function p13gotUploadData(data) {
|
||
var cmd = JSON.parse(data);
|
||
if ((uploadFile == null) || (parseInt(uploadFile.xfilePtr) != parseInt(cmd.reqid))) { return; }
|
||
|
||
if (cmd.action == 'uploadstart') {
|
||
p13uploadNextPart(false);
|
||
for (var i = 0; i < 8; i++) { p13uploadNextPart(true); } // Send 8 more blocks of 4 k to full the websocket.
|
||
} else if (cmd.action == 'uploadack') {
|
||
p13uploadNextPart(false);
|
||
} else if (cmd.action == 'uploaderror') {
|
||
p13uploadFileCancel();
|
||
}
|
||
}
|
||
|
||
// 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;
|
||
var start = uploadFile.xptr;
|
||
var end = uploadFile.xptr + 4096;
|
||
if (end > data.byteLength) { if (dataPriming == true) { return; } end = data.byteLength; }
|
||
if (start == data.byteLength) {
|
||
if (uploadFile.ws != null) { uploadFile.ws.Stop(); uploadFile.ws = null; }
|
||
if (uploadFile.xfiles.length > uploadFile.xfilePtr + 1) { p13uploadReconnect(); } else { p13uploadFileCancel(); }
|
||
} else {
|
||
var datapart = data.slice(start, end);
|
||
uploadFile.ws.send(datapart);
|
||
uploadFile.xptr = end;
|
||
Q('d2progressBar').value = end;
|
||
}
|
||
}
|
||
|
||
//
|
||
// DEVICE EVENTS
|
||
//
|
||
|
||
var currentDeviceEvents = null;
|
||
function deviceEventsUpdate() {
|
||
var x = '', dateHeader = null;
|
||
for (var i in currentDeviceEvents) {
|
||
var event = currentDeviceEvents[i];
|
||
var time = new Date(event.time);
|
||
if (printDate(time) != dateHeader) {
|
||
if (dateHeader != null) x += '</table>';
|
||
dateHeader = printDate(time);
|
||
x += '<table style=width:100% cellpadding=0 cellspacing=0><tr><td class=DevSt colspan=4>' + dateHeader + '</td></tr>';
|
||
}
|
||
var icon = 'si3';
|
||
if (event.etype == 'user') icon = 'm2';
|
||
if (event.etype == 'server') icon = 'si3';
|
||
|
||
var msg = event.msg.split('(R)').join('®');
|
||
//if (event.username && event.username != userinfo.name) { msg += ': ' + event.username; }
|
||
x += '<tr><td style=width:18px><div class=' + icon + '></div></td><td class=g1 style=float:none> </td><td style=background-color:#C9C9C9>' + printTime(time) + ' - ' + msg + '</td><td class=g2 style=float:none> </td></tr><tr style=height:2px></tr>';
|
||
}
|
||
if (dateHeader != null) x += '</table>';
|
||
if (x == '') x = "<br><i>No Events Found</i><br><br>";
|
||
QH('p16events', x);
|
||
}
|
||
|
||
/*
|
||
function showDeleteAllEventsDialog() {
|
||
if (xxdialogMode) return;
|
||
var x = "Delete all events in the server event log?<br /><br />";
|
||
x += "<input id=p3check type=checkbox onchange=validateDeleteAllEventsDialog() />Confirm";
|
||
setDialogMode(2, "Delete All Events", 3, showDeleteAllEventsDialogEx, x);
|
||
validateDeleteAllEventsDialog();
|
||
}
|
||
|
||
function validateDeleteAllEventsDialog() {
|
||
QE('idx_dlgOkButton', Q('p3check').checked);
|
||
}
|
||
|
||
function showDeleteAllEventsDialogEx(buttons, tag) {
|
||
meshserver.send({ action: 'clearevents' });
|
||
}
|
||
*/
|
||
|
||
function refreshDeviceEvents() {
|
||
//currentDeviceEvents = null;
|
||
//QH('p16events', '');
|
||
meshserver.send({ action: 'events', nodeid: currentNode._id, limit: parseInt(p16limitdropdown.value) });
|
||
}
|
||
|
||
//
|
||
// CONSOLE
|
||
//
|
||
|
||
function agentConsoleHandleKeys(e) {
|
||
if ((e.ctrlKey) || (e.altKey)) { return true; }
|
||
var processed = 0, box = Q('p15consoleText');
|
||
if (e.key) {
|
||
if (e.keyCode == 13 && consoleFocus == 0) { p15consoleSend(e); processed = 1; }
|
||
else if (e.keyCode == 8 && consoleFocus == 0) { var x = box.value; box.value = x.substring(0, x.length - 1); processed = 1; }
|
||
else if (e.keyCode == 27) { box.value = ''; processed = 1; }
|
||
else if ((e.keyCode == 38) || (e.keyCode == 40)) { // Arrow up || Arrow down
|
||
var hindex = consoleHistory.indexOf(box.value);
|
||
//console.log(hindex, consoleHistory);
|
||
if ((e.keyCode == 38) && ((consoleHistory.length - 1) > hindex)) { box.value = consoleHistory[hindex + 1]; }
|
||
else if ((e.keyCode == 40) && (hindex > 0)) { box.value = consoleHistory[hindex - 1]; }
|
||
else if ((e.keyCode == 40) && (hindex == 0)) { box.value = ''; }
|
||
processed = 1;
|
||
}
|
||
else if (e.key.length === 1) {
|
||
//box.value = ((box.value + e.key));
|
||
insertTextAtCursor(box, e.key);
|
||
processed = 1;
|
||
}
|
||
} else {
|
||
if (e.charCode != 0 && consoleFocus == 0) { box.value = ((box.value + String.fromCharCode(e.charCode))); processed = 1; }
|
||
}
|
||
if (processed > 0) { return haltEvent(e); }
|
||
}
|
||
|
||
// Insert text at the cursor location on the
|
||
function insertTextAtCursor(ctrl, val) {
|
||
if (document.selection) { ctrl.focus(); sel = document.selection.createRange(); sel.text = val; }
|
||
else if (ctrl.selectionStart || ctrl.selectionStart == '0') {
|
||
var start = ctrl.selectionStart, end = ctrl.selectionEnd;
|
||
ctrl.value = ctrl.value.substring(0, start) + val + ctrl.value.substring(end, ctrl.value.length);
|
||
ctrl.setSelectionRange(end + 1, end + 1);
|
||
} else { ctrl.value += myValue; }
|
||
}
|
||
|
||
var consoleNode;
|
||
var consoleServerText = '';
|
||
function setupConsole() {
|
||
if (xxcurrentView == 115) {
|
||
// Setup server console
|
||
var samenode = (consoleNode == 'server');
|
||
consoleNode = 'server';
|
||
|
||
QH('p15deviceName', 'My Server Console');
|
||
QE('p15consoleText', true);
|
||
QH('p15statetext', '');
|
||
QH('p15coreName', '');
|
||
|
||
if (samenode == false) {
|
||
QH('p15agentConsoleText', consoleServerText);
|
||
Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight;
|
||
}
|
||
} else {
|
||
// Setup the console
|
||
var samenode = (consoleNode == currentNode);
|
||
consoleNode = currentNode;
|
||
|
||
var mesh = meshes[consoleNode.meshid];
|
||
var meshrights = mesh.links[userinfo._id].rights;
|
||
if ((meshrights & 16) != 0) {
|
||
if (consoleNode.consoleText == null) { consoleNode.consoleText = ''; }
|
||
if (samenode == false) {
|
||
QH('p15agentConsoleText', consoleNode.consoleText);
|
||
Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight;
|
||
}
|
||
var online = ((consoleNode.conn & 1) != 0) ? true : false;
|
||
QH('p15statetext', online ? "Agent is online" : "Agent is offline");
|
||
QE('p15consoleText', online);
|
||
QE('p15uploadCore', online);
|
||
} else {
|
||
QH('p15statetext', 'Access Denied');
|
||
QE('p15consoleText', false);
|
||
QE('p15uploadCore', false);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Clear the console for this node
|
||
function p15consoleClear() {
|
||
QH('p15agentConsoleText', '');
|
||
Q('id_p15consoleClear').blur();
|
||
if (xxcurrentView == 115) {
|
||
consoleServerText = '';
|
||
} else {
|
||
consoleNode.consoleText = '';
|
||
}
|
||
}
|
||
|
||
// Send a command to the agent
|
||
var consoleHistory = [];
|
||
function p15consoleSend(e) {
|
||
if (e && e.keyCode != 13) return;
|
||
var v = Q('p15consoleText').value, t = '<div style=color:green>> ' + EscapeHtml(Q('p15consoleText').value) + '<br/></div>';
|
||
Q('p15agentConsoleText').innerHTML += t;
|
||
Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight;
|
||
Q('p15consoleText').value = '';
|
||
|
||
if (xxcurrentView == 115) {
|
||
// Send the command to the server - TODO: In the future, we may support multiple servers.
|
||
consoleServerText += t;
|
||
meshserver.send({ action: 'serverconsole', value: v });
|
||
} else {
|
||
// Send the command to the mesh agent
|
||
consoleNode.consoleText += t;
|
||
meshserver.send({ action: 'msg', type: 'console', nodeid: consoleNode._id, value: v });
|
||
}
|
||
|
||
// Add command to history list
|
||
if (v.length > 0) {
|
||
// Move this command to the top if it already exists
|
||
var j = consoleHistory.indexOf(v);
|
||
if (j >= 0) { consoleHistory.splice(j, 1); }
|
||
consoleHistory.unshift(v);
|
||
consoleHistory.splice(10);
|
||
}
|
||
}
|
||
|
||
// Handle Mesh Agent console data
|
||
function p15consoleReceive(node, data) {
|
||
data = '<div>' + data + '</div>'
|
||
if (node === 'serverconsole') {
|
||
// Server console data
|
||
consoleServerText += data;
|
||
if (consoleNode == 'server') {
|
||
Q('p15agentConsoleText').innerHTML += data;
|
||
Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight;
|
||
}
|
||
} else {
|
||
// Agent console data
|
||
if (node.consoleText == null) { node.consoleText = data; } else { node.consoleText += data; }
|
||
if (consoleNode == node) {
|
||
Q('p15agentConsoleText').innerHTML += data;
|
||
Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Save console text to file
|
||
function p15downloadConsoleText() {
|
||
saveAs(new Blob([Q('p15agentConsoleText').innerText], { type: "application/octet-stream" }), "console.txt");
|
||
}
|
||
|
||
// Called then user presses the "Change Core" button
|
||
function p15uploadCore(e) {
|
||
if (xxdialogMode) return;
|
||
if (e.shiftKey == true) { meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'default' }); } // Upload default core
|
||
else if (e.altKey == true) { meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'clear' }); } // Clear the core
|
||
else if (e.ctrlKey == true) { p15uploadCore2(); } // Upload the core from a file
|
||
else { setDialogMode(2, "Perform Agent Action", 3, p15uploadCoreEx, addHtmlValue('Action', '<select id=d3coreMode style=width:230px><option value=1>Upload default server core</option><option value=2>Clear the core</option><option value=6>Upload recovery core</option><option value=3>Upload a core file</option><option value=4>Soft disconnect agent</option><option value=5>Hard disconnect agent</option></select>')); }
|
||
}
|
||
|
||
function p15uploadCoreEx() {
|
||
if (Q('d3coreMode').value == 1) {
|
||
// Upload default core
|
||
meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'default' });
|
||
} else if (Q('d3coreMode').value == 2) {
|
||
// Clear the core
|
||
meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'clear' });
|
||
} else if (Q('d3coreMode').value == 3) {
|
||
// Upload file as core
|
||
p15uploadCore2();
|
||
} else if (Q('d3coreMode').value == 4) {
|
||
// Soft disconnect the mesh agent
|
||
meshserver.send({ action: 'agentdisconnect', nodeid: consoleNode._id, disconnectMode: 1 });
|
||
} else if (Q('d3coreMode').value == 5) {
|
||
// Hard disconnect the mesh agent
|
||
meshserver.send({ action: 'agentdisconnect', nodeid: consoleNode._id, disconnectMode: 2 });
|
||
} else if (Q('d3coreMode').value == 6) {
|
||
// Upload a recovery core
|
||
meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type:'recovery' });
|
||
}
|
||
}
|
||
|
||
// Called then user opts to upload a file as core
|
||
function p15uploadCore2() {
|
||
if (xxdialogMode) return;
|
||
Q('d3localmodeform').action = 'uploadmeshcorefile.ashx';
|
||
Q('d3attrib').value = currentNode._id;
|
||
setDialogMode(3, "Upload Mesh Agent Core", 3, p15uploadCoreEx2);
|
||
d3init();
|
||
}
|
||
|
||
function p15uploadCoreEx2() {
|
||
var mode = Q('d3uploadMode').value;
|
||
if (mode == 1) {
|
||
// Upload local mesh agent core
|
||
Q('d3submit').click();
|
||
} else {
|
||
// Upload server mesh agent code
|
||
var files = d3getFileSel();
|
||
if (files.length == 1) { meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'custom', path: d3filetreelocation.join('/') + '/' + files[0] }); }
|
||
}
|
||
}
|
||
|
||
//
|
||
// MY ACCOUNT
|
||
//
|
||
|
||
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)) return;
|
||
if ((userinfo.otpsecret == 1) || (userinfo.otphkeys > 0)) { meshserver.send({ action: 'otpauth-getpasswords', subaction: action }); }
|
||
}
|
||
|
||
function account_manageHardwareOtp() {
|
||
if ((xxdialogMode == 2) && (xxdialogTag == 'otpauth-hardware-manage')) { dialogclose(0); }
|
||
if (xxdialogMode || ((features & 4096) == 0)) return;
|
||
meshserver.send({ action: 'otp-hkey-get' });
|
||
}
|
||
|
||
function account_addhkey(type) {
|
||
if (type == 3) {
|
||
var x = "Type in the name of the key to add.<br /><br />";
|
||
x += addHtmlValue('Key Name', '<input id=dp1keyname style=width:230px maxlength=20 autocomplete=off placeholder="MyKey" onkeyup=account_addhkeyValidate(event,2) />');
|
||
} else if (type == 2) {
|
||
var x = "Type in a key name, select the OTP box and press the button on the YubiKey™.<br /><br />";
|
||
x += addHtmlValue('Key Name', '<input id=dp1keyname style=width:230px maxlength=20 autocomplete=off placeholder="MyKey" onkeyup=account_addhkeyValidate(event,1) />');
|
||
x += addHtmlValue('YubiKey™ OTP', '<input id=dp1key style=width:230px autocomplete=off onkeyup=account_addhkeyValidate(event,2) />');
|
||
}
|
||
setDialogMode(2, "Add Security Key", 3, account_addhkeyEx, x, type);
|
||
Q('dp1keyname').focus();
|
||
}
|
||
|
||
function account_addhkeyValidate(e,action) {
|
||
if ((e != null) && (e.keyCode == 13)) { if (action == 2) { dialogclose(1); } else { Q('dp1key').focus(); } }
|
||
}
|
||
|
||
function account_addhkeyEx(button, type) {
|
||
var name = Q('dp1keyname').value;
|
||
if (name == '') { name = 'MyKey'; }
|
||
if (type == 2) {
|
||
meshserver.send({ action: 'otp-hkey-yubikey-add', name: name, otp: Q('dp1key').value });
|
||
setDialogMode(2, "Add Security Key", 0, null, "<br />Checking...<br /><br /><br />", 'otpauth-hardware-manage');
|
||
} else if (type == 3) {
|
||
meshserver.send({ action: 'webauthn-startregister', name: name });
|
||
}
|
||
}
|
||
|
||
function account_removehkey(index) {
|
||
meshserver.send({ action: 'otp-hkey-remove', index: index });
|
||
meshserver.send({ action: 'otp-hkey-get' });
|
||
}
|
||
|
||
function account_enableNotifications() {
|
||
if (Notification) { Notification.requestPermission().then(function (permission) { QV('accountEnableNotificationsSpan', permission != "granted"); }); }
|
||
}
|
||
|
||
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 = "Change your account email address here.<br /><br />";
|
||
x += addHtmlValue('Email', '<input id=dp2email style=width:230px maxlength=256 onchange=account_validateEmail() onkeyup=account_validateEmail(event) />');
|
||
setDialogMode(2, "Email Address Change", 3, account_changeEmail, x);
|
||
if (userinfo.email != null) { Q('dp2email').value = userinfo.email; }
|
||
account_validateEmail();
|
||
Q('dp2email').focus();
|
||
}
|
||
|
||
function account_validateEmail(e, email) {
|
||
QE('idx_dlgOkButton', validateEmail(Q('dp2email').value) && (Q('dp2email').value != userinfo.email));
|
||
if ((e != null) && (e.keyCode == 13)) { dialogclose(1); }
|
||
}
|
||
|
||
function account_changeEmail() {
|
||
meshserver.send({ action: 'changeemail', email: Q('dp2email').value });
|
||
}
|
||
|
||
function account_showDeleteAccount() {
|
||
if (xxdialogMode) return;
|
||
var x = "To delete this account, type in the account password in both boxes below and hit ok.<br /><br />";
|
||
x += "<form action='" + domainUrl + "deleteaccount' method=post><table style=margin-left:80px><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><br /><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;
|
||
var x = "Change your account password by entering the old password and new password twice in the boxes below.";
|
||
if (features & 0x00010000) { " Password hint can be used but is not recommanded."; }
|
||
x += "<br /><br />";;
|
||
//x += "<form action='" + domainUrl + "changepassword' method=post>";
|
||
x += "<table style=margin-left:60px>";
|
||
x += "<tr><td align=right>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>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>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>Requirements: ' + r.join(', ') + '.</span>'; }
|
||
}
|
||
x += '<br />';
|
||
//x += '<br /><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, "Change Password", 3, account_showChangePasswordEx, x);
|
||
Q('apassword0').focus();
|
||
account_validateNewPassword();
|
||
}
|
||
|
||
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\" tab 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))) { 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\" tab and look at the \"Account Security\" section."); return; }
|
||
|
||
// We are allowed, let's prompt to information
|
||
var x = "Create a new device group using the options below.<br /><br />";
|
||
x += addHtmlValue('Name', '<input id=dp2meshname style=width:230px maxlength=64 onchange=account_validateMeshCreate() onkeyup=account_validateMeshCreate() />');
|
||
x += addHtmlValue('Type', '<div style=width:230px;margin:0;padding:0><select id=dp2meshtype style=width:100% onchange=account_validateMeshCreate() ><option value=2>Manage using a software agent</option><option value=1>Intel® AMT only, no agent</option></select></div>');
|
||
x += addHtmlValue('Description', '<div style=width:230px;margin:0;padding:0><textarea id=dp2meshdesc maxlength=1024 style=width:100%;resize:none></textarea></div>');
|
||
setDialogMode(2, "New Device Group", 3, account_createMeshEx, x);
|
||
account_validateMeshCreate();
|
||
Q('dp2meshname').focus();
|
||
}
|
||
|
||
function account_validateMeshCreate() {
|
||
QE('idx_dlgOkButton', Q('dp2meshname').value.length > 0);
|
||
}
|
||
|
||
function account_createMeshEx(button, tag) {
|
||
meshserver.send({ action: 'createmesh', meshname: Q('dp2meshname').value, meshtype: Q('dp2meshtype').value, desc: Q('dp2meshdesc').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>Good<span>'; } else { r = '<span style=color:red>Weak<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 num = 0, lower = 0, upper = 0, nonalpha = 0;
|
||
for (var i = 0; i < password.length; i++) {
|
||
if (/\d/.test(password[i])) { num++; }
|
||
if (/[a-z]/.test(password[i])) { lower++; }
|
||
if (/[A-Z]/.test(password[i])) { upper++; }
|
||
if (/\W/.test(password[i])) { nonalpha++; }
|
||
}
|
||
if (requirements.num && (num < requirements.num)) 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 = '';
|
||
var c = 0, count = 0;
|
||
for (i in meshes) {
|
||
// Mesh positioning
|
||
if (c > 1) { r += '</tr><tr>'; c = 0; }
|
||
c++;
|
||
count++;
|
||
|
||
// Mesh rights
|
||
var meshrights = 0;
|
||
if (meshes[i].links[userinfo._id]) { meshrights = meshes[i].links[userinfo._id].rights; }
|
||
var rights = 'Partial Rights';
|
||
if (meshrights == 0xFFFFFFFF) rights = 'Full Administrator'; else if (meshrights == 0) rights = 'No Rights';
|
||
|
||
// Print the mesh information
|
||
r += '<div onmouseover=devMouseHover(this,1) onmouseout=devMouseHover(this,0) style=display:inline-block;width:431px;height:50px;padding-top:1px;padding-bottom:1px;float:left><div style=float:left;width:30px;height:100%></div><div style=height:100%;cursor:pointer onclick=gotoMesh(\'' + i + '\')><div class=mi style=float:left;width:50px;height:50px></div><div style=height:100%><div class=g1></div><div class=e2 style=width:300px><div class=e1>' + EscapeHtml(meshes[i].name) + '</div><div>' + rights + '</div></div><div class=g2 style=float:left></div></div></div></div>';
|
||
}
|
||
|
||
meshcount = count;
|
||
QH('p2meshes', r);
|
||
QV('p2noMeshFound', count == 0);
|
||
}
|
||
|
||
function gotoMesh(meshid) {
|
||
currentMesh = meshes[meshid];
|
||
p20updateMesh();
|
||
go(20);
|
||
}
|
||
|
||
function server_showRestoreDlg() {
|
||
if (xxdialogMode) return;
|
||
var x = 'Restore the server using a backup, <span style=color:red>this will delete the existing server data</span>. Only do this if you know what you are doing.<br /><br />';
|
||
x += '<form action="/restoreserver.ashx" enctype="multipart/form-data" method="post"><div>';
|
||
x += '<input id=account_dlgFileInput type=file name=datafile style=width:100% accept=".zip,application/octet-stream,application/zip,application/x-zip,application/x-zip-compressed" onchange=account_validateServerRestore()>';
|
||
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 /><br /></form>';
|
||
setDialogMode(2, "Restore Server", 0, null, x);
|
||
account_validateServerRestore();
|
||
}
|
||
|
||
function account_validateServerRestore() {
|
||
QE('account_dlgOkButton', Q('account_dlgFileInput').files.length == 1);
|
||
}
|
||
|
||
function server_showVersionDlg() {
|
||
if (xxdialogMode) return;
|
||
setDialogMode(2, "MeshCentral Version", 1, null, "Loading...", 'MeshCentralServerUpdate');
|
||
meshserver.send({ action: 'serverversion' });
|
||
}
|
||
|
||
function server_showVersionDlgUpdate() { QE('idx_dlgOkButton', Q('d2updateCheck').checked); }
|
||
function server_showVersionDlgEx() { meshserver.send({ action: 'serverupdate' }); }
|
||
|
||
function server_showErrorsDlg() {
|
||
if (xxdialogMode) return;
|
||
setDialogMode(2, "MeshCentral Errors", 1, null, "Loading...", 'MeshCentralServerErrors');
|
||
meshserver.send({ action: 'servererrors' });
|
||
}
|
||
function server_showErrorsDlgUpdate() { QE('idx_dlgOkButton', Q('d2updateCheck').checked); }
|
||
function server_showErrorsDlgEx() { meshserver.send({ action: 'serverclearerrorlog' }); }
|
||
|
||
//
|
||
// MY MESHS
|
||
//
|
||
|
||
var currentMesh;
|
||
function p20updateMesh() {
|
||
if (currentMesh == null) return;
|
||
QH('p20meshName', EscapeHtml(currentMesh.name));
|
||
var meshtype = 'Unknown #' + currentMesh.mtype;
|
||
var meshrights = 0;
|
||
try { meshrights = currentMesh.links[userinfo._id].rights; } catch (ex) { }
|
||
if (currentMesh.mtype == 1) meshtype = 'Intel® AMT only, no agent';
|
||
if (currentMesh.mtype == 2) meshtype = 'Managed using a software agent';
|
||
|
||
var x = '';
|
||
x += addHtmlValue('Name', addLinkConditional(EscapeHtml(currentMesh.name), 'p20editmesh(1)', (meshrights & 1) != 0));
|
||
x += addHtmlValue('Description', addLinkConditional(((currentMesh.desc && currentMesh.desc != '')?EscapeHtml(currentMesh.desc):'<i>None</i>'), 'p20editmesh(2)', (meshrights & 1) != 0));
|
||
|
||
// Display group type
|
||
x += addHtmlValue('Type', meshtype);
|
||
//x += addHtmlValue('Identifier', currentMesh._id.split('/')[2]);
|
||
|
||
// Display features
|
||
var meshFeatures = [];
|
||
if (currentMesh.flags) {
|
||
if (currentMesh.flags & 1) { meshFeatures.push('Auto-Remove'); }
|
||
if (currentMesh.flags & 2) { meshFeatures.push('Hostname Sync'); }
|
||
}
|
||
meshFeatures = meshFeatures.join(', ');
|
||
if (meshFeatures == '') { meshFeatures = '<i>None</i>'; }
|
||
x += addHtmlValue('Features', addLinkConditional(meshFeatures, 'p20editmeshfeatures()', meshrights & 1));
|
||
|
||
// Display user consent
|
||
if (currentMesh.mtype == 2) {
|
||
meshFeatures = [];
|
||
var consent = 0;
|
||
if (currentMesh.consent) { consent = currentMesh.consent; }
|
||
if (serverinfo.consent) { consent |= serverinfo.consent; }
|
||
if (consent & 0x0008) { meshFeatures.push('Desktop Prompt'); } else { if (consent & 0x0001) { meshFeatures.push('Desktop Notify'); } }
|
||
if (consent & 0x0010) { meshFeatures.push('Terminal Prompt'); } else { if (consent & 0x0002) { meshFeatures.push('Terminal Notify'); } }
|
||
if (consent & 0x0020) { meshFeatures.push('Files Prompt'); } else { if (consent & 0x0004) { meshFeatures.push('Files Notify'); } }
|
||
if (consent == 7) { meshFeatures = ['Always Notify']; }
|
||
if ((consent & 56) == 56) { meshFeatures = ['Always Prompt']; }
|
||
|
||
meshFeatures = meshFeatures.join(', ');
|
||
if (meshFeatures == '') { meshFeatures = '<i>None</i>'; }
|
||
x += addHtmlValue('User Consent', addLinkConditional(meshFeatures, 'p20editmeshconsent()', meshrights & 1));
|
||
}
|
||
|
||
// Intel AMT setup
|
||
if (currentMesh.mtype == 2) {
|
||
var intelAmtPolicy = 'No Policy';
|
||
if (currentMesh.amt) {
|
||
if (currentMesh.amt.type == 1) { intelAmtPolicy = 'Deactivate Client Control Mode (CCM)'; }
|
||
else if (currentMesh.amt.type == 2) {
|
||
intelAmtPolicy = 'Simple Client Control Mode (CCM)';
|
||
if (currentMesh.amt.cirasetup == 2) { intelAmtPolicy += ' + CIRA'; }
|
||
} else if (currentMesh.amt.type == 3) {
|
||
intelAmtPolicy = 'Simple Admin Control Mode (ACM)';
|
||
if (currentMesh.amt.cirasetup == 2) { intelAmtPolicy += ' + CIRA'; }
|
||
}
|
||
}
|
||
x += addHtmlValue('Intel® AMT', addLinkConditional(intelAmtPolicy, 'p20editMeshAmt()', meshrights & 1));
|
||
}
|
||
|
||
// Display group note support
|
||
if (meshrights & 1) { x += '<br><input type=button value=Notes title="View notes about this device group" 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 += '<a onclick=p20showAddMeshUserDialog() style=cursor:pointer;margin-right:10px><img src=images/icon-addnew.png border=0 height=12 width=12> Add Users</a>'; }
|
||
|
||
if ((meshrights & 4) != 0) {
|
||
if (currentMesh.mtype == 1) {
|
||
x += '<a onclick=addCiraDeviceToMesh(\"' + currentMesh._id + '\") style=cursor:pointer;margin-right:10px title="Add a new Intel® AMT computer that is located on the internet."><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 title="Add a new Intel® AMT computer that is located on the local network."><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 title="Add a new computer to this mesh by installing the mesh agent."><img src=images/icon-addnew.png border=0 height=12 width=12> Install</a>';
|
||
x += '<a onclick=inviteAgentToMesh(\"' + currentMesh._id + '\") style=cursor:pointer;margin-right:10px title="Invite someone to install the mesh agent on this mesh."><img src=images/icon-addnew.png border=0 height=12 width=12> Invite</a>';
|
||
}
|
||
}
|
||
|
||
/*
|
||
function getMeshActions(mesh, meshrights) {
|
||
if ((meshrights & 4) == 0) return '';
|
||
var r = '';
|
||
if (mesh.mtype == 1) {
|
||
r += ' <a style=cursor:pointer;font-size:10px title="Add a new Intel® AMT computer that is located on the internet." onclick=addCiraDeviceToMesh(\"' + mesh._id + '\")>Add CIRA</a>';
|
||
r += ' <a style=cursor:pointer;font-size:10px title="Add a new Intel® AMT computer that is located on the local network." onclick=addDeviceToMesh(\"' + mesh._id + '\")>Add Local</a>';
|
||
}
|
||
if (mesh.mtype == 2) {
|
||
r += ' <a style=cursor:pointer;font-size:10px title="Add a new computer to this mesh by installing the mesh agent." onclick=addAgentToMesh(\"' + mesh._id + '\")>Add Agent</a>';
|
||
}
|
||
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><th scope=col style=text-align:left></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; }
|
||
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;
|
||
if (r == 0xFFFFFFFF) rights = 'Full Administrator'; else if (r == 0) rights = 'No Rights';
|
||
if ((sortedusers[i].id != userinfo._id) && (meshrights == 0xFFFFFFFF || (((meshrights & 2) != 0)))) { trash = '<a onclick=p20deleteUser(event,"' + encodeURIComponent(sortedusers[i].id) + '") title="Remote user rights to this mesh" style=cursor:pointer><img src=images/trash.png border=0 height=10 width=10></a>'; }
|
||
x += '<tr onclick=p20viewuser("' + encodeURIComponent(sortedusers[i].id) + '") style=cursor:pointer' + (((count % 2) == 0) ? ';background-color:#DDD' : '') + '><td><div title="User" class=m2></div><div> ' + EscapeHtml(decodeURIComponent(sortedusers[i].name)) + '<div></div></div></td><td><div style=float:right>' + trash + '</div><div>' + rights + '</div></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:x-small;text-align:right><span><a onclick=p20showDeleteMeshDialog() style=cursor:pointer>Delete Group</a></span></div>'; }
|
||
|
||
QH('p20info', x);
|
||
}
|
||
|
||
function p20editMeshAmt() {
|
||
if (xxdialogMode) return;
|
||
var x = '', acmoption = '';
|
||
if ((features & 0x100000) != 0) { acmoption = '<option value=3>Simple Admin Control Mode (ACM)</option>'; }
|
||
x += addHtmlValue('Type', '<select id=dp20amtpolicy style=width:230px onchange=p20editMeshAmtChange()><option value=0>No Policy</option><option value=1>Deactivate Client Control Mode (CCM)</option><option value=2>Simple Client Control Mode (CCM)</option>' + acmoption + '</select>');
|
||
x += '<div id=dp20amtpolicydiv></div>';
|
||
setDialogMode(2, "Intel® AMT Policy", 3, p20editMeshAmtEx, x);
|
||
if (currentMesh.amt) { Q('dp20amtpolicy').value = currentMesh.amt.type; }
|
||
p20editMeshAmtChange();
|
||
|
||
// Set the current Intel AMT policy
|
||
if (currentMesh.amt && (currentMesh.amt.type == 2) || (currentMesh.amt.type == 3)) {
|
||
Q('dp20amtpolicypass').value = currentMesh.amt.password;
|
||
if (currentMesh.amt.type == 2) { Q('dp20amtbadpass').value = currentMesh.amt.badpass; }
|
||
if ((features & 0x400) == 0) { Q('dp20amtcira').value = currentMesh.amt.cirasetup; }
|
||
}
|
||
|
||
dp20amtValidatePolicy();
|
||
}
|
||
|
||
function p20editMeshAmtChange() {
|
||
var ptype = Q('dp20amtpolicy').value, x = '';
|
||
if (ptype >= 2) {
|
||
x = addHtmlValue('Password*', '<input id=dp20amtpolicypass type=password style=width:230px maxlength=32 onchange=dp20amtValidatePolicy() onkeyup=dp20amtValidatePolicy() />')
|
||
x += addHtmlValue('Password*', '<input id=dp20amtpolicypass2 type=password style=width:230px maxlength=32 onchange=dp20amtValidatePolicy() onkeyup=dp20amtValidatePolicy() />')
|
||
if (ptype == 2) { x += addHtmlValue('Password mismatch', "<select id=dp20amtbadpass style=width:230px><option value=0>Do nothing</option><option value=1>Reactivate Intel® AMT</option></select>"); }
|
||
if ((features & 0x400) == 0) {
|
||
if (ptype == 2) {
|
||
x += addHtmlValue('<span title="Client Initiated Remote Access">CIRA</span>', "<select id=dp20amtcira style=width:230px><option value=0>Don't configure</option><option value=1>Don't connect to server</option><option value=2>Connect to server</option></select>");
|
||
} else {
|
||
x += addHtmlValue('<span title="Client Initiated Remote Access">CIRA</span>', "<select id=dp20amtcira style=width:230px><option value=0>Don't configure</option><option value=2>Connect to server</option></select>");
|
||
}
|
||
}
|
||
x += '<br/><span style="font-size:10px">* Recommanded, leave blank to assign a random password to each device.</span><br/>';
|
||
if (ptype == 2) {
|
||
x += '<span style="font-size:10px">This policy will not impact devices with Intel® AMT in ACM mode.</span><br/>';
|
||
x += '<span style="font-size:10px">This is not a secure policy as agents will be performing activation.</span>';
|
||
} else {
|
||
x += '<span style="font-size:10px">During activation, the agent will have access to admin password infomation.</span>';
|
||
}
|
||
}
|
||
QH('dp20amtpolicydiv', x);
|
||
}
|
||
|
||
function dp20amtValidatePolicy() {
|
||
var ok = true, ptype = Q('dp20amtpolicy').value;
|
||
if ((ptype == 2) || (ptype == 3)) {
|
||
var pass = Q('dp20amtpolicypass').value, pass2 = Q('dp20amtpolicypass2').value;
|
||
ok = ((pass === pass2) && ((pass === '') ? true : passwordcheck(pass)));
|
||
}
|
||
QE('idx_dlgOkButton', ok);
|
||
}
|
||
|
||
function p20editMeshAmtEx() {
|
||
var ptype = parseInt(Q('dp20amtpolicy').value), amtpolicy = { type: ptype };
|
||
if (ptype == 2) {
|
||
amtpolicy = { type: ptype, password: Q('dp20amtpolicypass').value, badpass: parseInt(Q('dp20amtbadpass').value) };
|
||
if ((features & 0x400) == 0) { amtpolicy.cirasetup = parseInt(Q('dp20amtcira').value); } else { amtpolicy.cirasetup = 1; }
|
||
} else if (ptype == 3) {
|
||
amtpolicy = { type: ptype, password: Q('dp20amtpolicypass').value };
|
||
if ((features & 0x400) == 0) { amtpolicy.cirasetup = parseInt(Q('dp20amtcira').value); } else { amtpolicy.cirasetup = 1; }
|
||
}
|
||
meshserver.send({ action: 'meshamtpolicy', meshid: currentMesh._id, amtpolicy: amtpolicy });
|
||
}
|
||
|
||
function p20showDeleteMeshDialog() {
|
||
if (xxdialogMode) return;
|
||
var x = "Are you sure you want to delete group \"" + EscapeHtml(currentMesh.name) + "\"? Deleting the device group will also delete all information about devices within this group.<br /><br />";
|
||
x += "<input id=p20check type=checkbox onchange=p20validateDeleteMeshDialog() />Confirm";
|
||
setDialogMode(2, "Delete Group", 3, p20showDeleteMeshDialogEx, x);
|
||
p20validateDeleteMeshDialog();
|
||
}
|
||
|
||
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:230px maxlength=32 onchange=p20editmeshValidate() onkeyup=p20editmeshValidate() />');
|
||
x += addHtmlValue('Description', '<div style=width:230px;margin:0;padding:0><textarea id=dp20meshdesc maxlength=1024 style=width:100%;resize:none></textarea></div>');
|
||
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 p20editmeshconsent() {
|
||
if (xxdialogMode) return;
|
||
var x = '', consent = (currentMesh.consent) ? currentMesh.consent : 0;
|
||
x += '<div style="width:100%;border-bottom:1px solid gray;margin-bottom:5px"><b>Desktop</b></div>';
|
||
x += "<div><input type=checkbox id=d20flag1 " + ((consent & 0x0001) ? 'checked' : '') + ">Notify user</div>";
|
||
x += "<div><input type=checkbox id=d20flag2 " + ((consent & 0x0008) ? 'checked' : '') + ">Prompt for user consent</div>";
|
||
x += '<div style="width:100%;border-bottom:1px solid gray;margin-bottom:5px;margin-top:8px"><b>Terminal</b></div>';
|
||
x += "<div><input type=checkbox id=d20flag3 " + ((consent & 0x0002) ? 'checked' : '') + ">Notify user</div>";
|
||
x += "<div><input type=checkbox id=d20flag4 " + ((consent & 0x0010) ? 'checked' : '') + ">Prompt for user consent</div>";
|
||
x += '<div style="width:100%;border-bottom:1px solid gray;margin-bottom:5px;margin-top:8px"><b>Files</b></div>';
|
||
x += "<div><input type=checkbox id=d20flag5 " + ((consent & 0x0004) ? 'checked' : '') + ">Notify user</div>";
|
||
x += "<div><input type=checkbox id=d20flag6 " + ((consent & 0x0020) ? 'checked' : '') + ">Prompt for user consent</div>";
|
||
setDialogMode(2, "Edit Device Group User Consent", 3, p20editmeshconsentEx, x);
|
||
if (serverinfo.consent) {
|
||
if (serverinfo.consent & 0x0001) { Q('d20flag1').checked = true; }
|
||
if (serverinfo.consent & 0x0008) { Q('d20flag2').checked = true; }
|
||
if (serverinfo.consent & 0x0002) { Q('d20flag3').checked = true; }
|
||
if (serverinfo.consent & 0x0010) { Q('d20flag4').checked = true; }
|
||
if (serverinfo.consent & 0x0004) { Q('d20flag5').checked = true; }
|
||
if (serverinfo.consent & 0x0020) { Q('d20flag6').checked = true; }
|
||
QE('d20flag1', !(serverinfo.consent & 0x0001));
|
||
QE('d20flag2', !(serverinfo.consent & 0x0008));
|
||
QE('d20flag3', !(serverinfo.consent & 0x0002));
|
||
QE('d20flag4', !(serverinfo.consent & 0x0010));
|
||
QE('d20flag5', !(serverinfo.consent & 0x0004));
|
||
QE('d20flag6', !(serverinfo.consent & 0x0020));
|
||
}
|
||
}
|
||
|
||
function p20editmeshconsentEx() {
|
||
var consent = 0;
|
||
if (Q('d20flag1').checked) { consent += 0x0001; }
|
||
if (Q('d20flag2').checked) { consent += 0x0008; }
|
||
if (Q('d20flag3').checked) { consent += 0x0002; }
|
||
if (Q('d20flag4').checked) { consent += 0x0010; }
|
||
if (Q('d20flag5').checked) { consent += 0x0004; }
|
||
if (Q('d20flag6').checked) { consent += 0x0020; }
|
||
meshserver.send({ action: 'editmesh', meshid: currentMesh._id, consent: consent });
|
||
}
|
||
|
||
function p20editmeshfeatures() {
|
||
if (xxdialogMode) return;
|
||
var flags = (currentMesh.flags)?currentMesh.flags:0;
|
||
var x = "<div><input type=checkbox id=d20flag1 " + ((flags & 1)?'checked':'') + ">Remove device on disconnect<br></div>";
|
||
x += "<div><input type=checkbox id=d20flag2 " + ((flags & 2) ? 'checked' : '') + ">Sync server device name to hostname<br></div>";
|
||
setDialogMode(2, "Edit Device Group Features", 3, p20editmeshfeaturesEx, x);
|
||
}
|
||
|
||
function p20editmeshfeaturesEx() {
|
||
var flags = 0;
|
||
if (Q('d20flag1').checked) { flags += 1; }
|
||
if (Q('d20flag2').checked) { flags += 2; }
|
||
meshserver.send({ action: 'editmesh', meshid: currentMesh._id, flags: flags });
|
||
}
|
||
|
||
function p20showAddMeshUserDialog() {
|
||
if (xxdialogMode) return;
|
||
var x = "Allow users to manage this device group and devices in this group.";
|
||
if (features & 0x00080000) { x += " Users need to login to this server once before they can be added to a device group." }
|
||
x += "<br /><br /><div style='position:relative'>";
|
||
x += addHtmlValue('User Names', '<input id=dp20username style=width:230px maxlength=32 onchange=p20validateAddMeshUserDialog() onkeyup=p20validateAddMeshUserDialog() placeholder="user1, user2, user3" />');
|
||
x += "<div id=dp20usersuggest class=suggestionBox style='top:30px;left:130px;display:none'></div>";
|
||
x += '</div>';
|
||
x += '<br><div style="height:120px;overflow-y:scroll;border:1px solid gray">';
|
||
x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20fulladmin>Full Administrator<br>';
|
||
x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20editmesh>Edit Device Group<br>';
|
||
x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20manageusers>Manage Device Group Users<br>';
|
||
x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20managecomputers>Manage Device Group Computers<br>';
|
||
x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20remotecontrol>Remote Control<br>';
|
||
x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20remoteview style=margin-left:12px>Remote View Only<br>';
|
||
x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20remotelimitedinput style=margin-left:12px>Limited Input Only<br>';
|
||
x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20noterminal style=margin-left:12px>No Terminal Access<br>';
|
||
x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20nofiles style=margin-left:12px>No File Access<br>';
|
||
x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20noamt style=margin-left:12px>No Intel® AMT<br>';
|
||
x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20meshagentconsole>Mesh Agent Console<br>';
|
||
x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20meshserverfiles>Server Files<br>';
|
||
x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20wakedevices>Wake Devices<br>';
|
||
x += '<input type=checkbox onchange=p20validateAddMeshUserDialog() id=p20editnotes>Edit Device Notes<br>';
|
||
x += '</div>';
|
||
setDialogMode(2, "Add Users to Device Group", 3, p20showAddMeshUserDialogEx, x);
|
||
p20validateAddMeshUserDialog();
|
||
Q('dp20username').focus();
|
||
}
|
||
|
||
function p20setname(name) {
|
||
name = decodeURIComponent(name);
|
||
var xusers = Q('dp20username').value.split(',');
|
||
for (var i in xusers) { xusers[i] = xusers[i].trim(); }
|
||
xusers[xusers.length - 1] = name;
|
||
Q('dp20username').value = xusers.join(', ');
|
||
p20validateAddMeshUserDialog();
|
||
}
|
||
|
||
function p20validateAddMeshUserDialog() {
|
||
var meshrights = currentMesh.links[userinfo._id].rights;
|
||
var ok = true, xusers = Q('dp20username').value.split(',');
|
||
for (var i in xusers) { var xuser = xusers[i] = xusers[i].trim(); if (xuser.length == 0) { ok = false; } else if (xuser.indexOf('"') >= 0) { ok = false; } }
|
||
QE('idx_dlgOkButton', ok);
|
||
|
||
// Fill the suggestion box
|
||
var showsuggestbox = false, exactMatch = false;
|
||
if (users != null) {
|
||
var lastuser = xusers[xusers.length - 1].trim(), lastuserl = lastuser.toLowerCase(), matchingUsers = [];
|
||
if (lastuser.length > 0) {
|
||
for (var i in users) {
|
||
if (users[i].name === lastuser) { exactMatch = true; break; }
|
||
if (users[i].name.toLowerCase().indexOf(lastuserl) >= 0) { matchingUsers.push(users[i].name); if (matchingUsers.length >= 8) break; }
|
||
}
|
||
if ((exactMatch == false) && (matchingUsers.length > 0)) {
|
||
var x = '';
|
||
for (var i in matchingUsers) { x += '<a onclick=p20setname("' + encodeURIComponent(matchingUsers[i]) + '")>' + matchingUsers[i] + '</a><br />'; }
|
||
QH('dp20usersuggest', x);
|
||
showsuggestbox = true;
|
||
}
|
||
}
|
||
}
|
||
QV('dp20usersuggest', showsuggestbox);
|
||
|
||
QE('p20fulladmin', meshrights == 0xFFFFFFFF);
|
||
QE('p20editmesh', (!Q('p20fulladmin').checked) && (meshrights == 0xFFFFFFFF));
|
||
QE('p20manageusers', !Q('p20fulladmin').checked);
|
||
QE('p20managecomputers', !Q('p20fulladmin').checked);
|
||
QE('p20remotecontrol', !Q('p20fulladmin').checked);
|
||
QE('p20meshagentconsole', !Q('p20fulladmin').checked);
|
||
QE('p20meshserverfiles', !Q('p20fulladmin').checked);
|
||
QE('p20wakedevices', !Q('p20fulladmin').checked);
|
||
QE('p20editnotes', !Q('p20fulladmin').checked);
|
||
QE('p20remoteview', !Q('p20fulladmin').checked && Q('p20remotecontrol').checked);
|
||
QE('p20remotelimitedinput', !Q('p20fulladmin').checked && Q('p20remotecontrol').checked && !Q('p20remoteview').checked);
|
||
QE('p20noterminal', !Q('p20fulladmin').checked && Q('p20remotecontrol').checked);
|
||
QE('p20nofiles', !Q('p20fulladmin').checked && Q('p20remotecontrol').checked);
|
||
QE('p20noamt', !Q('p20fulladmin').checked && Q('p20remotecontrol').checked);
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
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 = currentMesh.links[userinfo._id].rights, meshrights = currentMesh.links[userid].rights;
|
||
if (meshrights == 0xFFFFFFFF) r = ', Full Administrator (all rights)'; else {
|
||
if ((meshrights & 1) != 0) r += ', Edit Device Group';
|
||
if ((meshrights & 2) != 0) r += ', Manage Device Group Users';
|
||
if ((meshrights & 4) != 0) r += ', Manage Device Group Computers';
|
||
if ((meshrights & 8) != 0) r += ', Remote Control';
|
||
if ((meshrights & 16) != 0) r += ', Agent Console';
|
||
if ((meshrights & 32) != 0) r += ', Server Files';
|
||
if ((meshrights & 64) != 0) r += ', Wake Devices';
|
||
if ((meshrights & 128) != 0) r += ', Edit Notes';
|
||
if (((meshrights & 8) != 0) && (meshrights & 256) != 0) r += ', Remote View Only';
|
||
if (((meshrights & 8) != 0) && (meshrights & 512) != 0) r += ', No Terminal';
|
||
if (((meshrights & 8) != 0) && (meshrights & 1024) != 0) r += ', No Files';
|
||
if (((meshrights & 8) != 0) && (meshrights & 2048) != 0) r += ', No Intel® AMT';
|
||
if (((meshrights & 8) != 0) && ((meshrights & 4096) != 0) && ((meshrights & 256) == 0)) r += ', Limited Input';
|
||
}
|
||
r = r.substring(2);
|
||
if (r == '') { r = 'No Rights'; }
|
||
var uname = userid.split('/')[2];
|
||
if (users && users[userid]) { uname = users[userid].name; }
|
||
if (userinfo._id == userid) { uname = userinfo.name; }
|
||
var buttons = 1, x = addHtmlValue('User Name', EscapeHtml(decodeURIComponent(uname)));
|
||
if (userid.split('/')[2] != uname) { x += addHtmlValue('User Identifier', EscapeHtml(userid.split('/')[2])); }
|
||
|
||
x += addHtmlValue('Permissions', r);
|
||
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 (userinfo._id == userid) { uname = userinfo.name; }
|
||
setDialogMode(2, "Remote Mesh User", 3, p20viewuserEx2, "Confirm removal of user " + EscapeHtml(decodeURIComponent(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 }); }
|
||
|
||
//
|
||
// MY FILES
|
||
//
|
||
|
||
var filetreelinkpath;
|
||
var filetreelocation = [];
|
||
|
||
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 + ')>' + (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 > 70) { shortname = '<span title="' + EscapeHtml(name) + '">' + EscapeHtml(name.substring(0, 70)) + "...</span>"; } else { shortname = EscapeHtml(name); }
|
||
name = EscapeHtml(name);
|
||
|
||
// Figure out the date
|
||
var fdatestr = '';
|
||
if (f.d != null) { var fdate = new Date(f.d), fdatestr = printDateTime(fdate) + " "; }
|
||
|
||
// Figure out the size
|
||
var fsize = '';
|
||
if (f.s != null) { fsize = getFileSizeStr(f.s); }
|
||
|
||
var h = '';
|
||
if (f.t < 3 || f.t == 4) {
|
||
var right = (f.t == 1 || f.t == 4)?p5getQuotabar(f):'', title = '';
|
||
h = "<div class=filelist file=999><input file=999 style=float:left name=fc class=fcb type=checkbox onchange=p5setActions() value='" + name + "'> <span style=float:right title=\"" + title + "\">" + right + "</span><span><div class=fileIcon" + f.t + " onclick=p5folderset(\"" + encodeURIComponent(f.nx) + "\")></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 title=\"Display public link\" onclick=\'p5showPublicLink(\"' + publicPath + '/' + f.nx + '\")\'>Link</a>)'; }
|
||
if (f.s > 0) { link = "<a rel=\"noreferrer noopener\" target=\"_blank\" href=\"downloadfile.ashx?link=" + encodeURIComponent(filetreelinkpath + '/' + f.nx) + "\">" + shortname + "</a>" + publiclink; }
|
||
h = "<div class=filelist file=3><input file=3 style=float:left name=fc class=fcb type=checkbox onchange=p5setActions() value='" + f.nx + "'> <span class=fsize>" + fdatestr + "</span><span style=float:right>" + 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 limit exceed';
|
||
if (bytes < 2048) return bytes + ' bytes remaining';
|
||
if (bytes < 2097152) return Math.round(bytes / 1024) + ' kilobytes remaining';
|
||
if (bytes < 2147483648) return Math.round(bytes / 1024 / 1024) + ' megabytes remaining';
|
||
return Math.round(bytes / 1024 / 1024 / 1024) + ' gigabytes remaining';
|
||
}
|
||
|
||
function getNiceSize2(bytes) {
|
||
if (bytes <= 0) return 'None';
|
||
if (bytes < 2048) return bytes + ' b';
|
||
if (bytes < 2097152) return Math.round(bytes / 1024) + ' Kb';
|
||
if (bytes < 2147483648) return Math.round(bytes / 1024 / 1024) + ' Mb';
|
||
return Math.round(bytes / 1024 / 1024 / 1024) + ' Gb';
|
||
}
|
||
|
||
function p5getQuotabar(f) {
|
||
while (f.t > 1 && f.t != 4) { f = f.parent; }
|
||
if ((f.t != 1 && f.t != 4) || (f.maxbytes == null)) return '';
|
||
var tf = Math.floor(f.s / 1024), tq = (f.maxbytes - f.s);
|
||
return '<span title="' + tf + "k in " + f.c + " file" + (f.c > 1 ? 's' : '') + ". " + (Math.floor(f.maxbytes / 1024 / 1024)) + 'k maxinum">' + getNiceSize(tq) + ' <progress style=height:10px;width:100px value=' + f.s + ' max=' + f.maxbytes + ' /></span>';
|
||
}
|
||
|
||
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 ? 'Select None' : 'Select 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 "" + size + " bytes"; }
|
||
function p5folderup(x) { if (x == null) { filetreelocation.pop(); } else { while (filetreelocation.length > x) { filetreelocation.pop(); } } updateFiles(); }
|
||
function p5folderset(x) { filetreelocation.push(decodeURIComponent(x)); updateFiles(); }
|
||
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 /><input type=checkbox id=p5recdeleteinput>Recursive delete<br>" : "<input type=checkbox id=p5recdeleteinput style='display:none'>"; setDialogMode(2, "Delete", 3, p5deletefileEx, (cc > 1) ? ('Delete ' + cc + ' selected items?' + 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=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 = 'Confim ' + (p5clipboardCut == 0?'copy':'move') + ' of ' + p5clipboard.length + ' entrie' + ((p5clipboard.length > 1)?'s':'') + ' to this location?' } 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 = 'Holding ' + p5clipboard.length + ' entrie' + ((p5clipboard.length > 1)?'s':'') + ' for ' + (p5clipboardCut == 0?'copy':'move') + ', <a onclick=p5clearClip() style=cursor:pointer>Clear</a>.' } QH('p5bottomstatus', x); p5setActions(); }
|
||
function p5clearClip() { p5clipboard = null; p5clipboardFolder = null; p5clipboardCut = 0; p5updateClipview(); }
|
||
|
||
function p5fileDragDrop(e) {
|
||
if (xxdialogMode) return;
|
||
haltEvent(e);
|
||
QV('bigfail', false);
|
||
QV('bigok', false);
|
||
//QV('p5fileCatchAllInput', false);
|
||
|
||
// For Chrome & Firefox
|
||
var error = 0;
|
||
p5uploadFile(); // Display the the dialog box
|
||
try { Q('p5uploadinput').files = e.dataTransfer.files; } catch (ex) { error = 1; } // Set the files in the dialog box
|
||
if (error == 0) { p5uploadFileEx(); } // Press the submit button
|
||
setDialogMode(0); // Close the dialog box
|
||
|
||
// For IE browser - This will not work with very large files
|
||
if (error == 1) {
|
||
if (e.dataTransfer == null || e.dataTransfer.files.length == 0 || filetreelocation.length == 0) return;
|
||
var names = [], sizes = [], types = [], datas = [], readercount = e.dataTransfer.files.length, totalSize = 0;
|
||
for (var i = 0; i < e.dataTransfer.files.length; i++) { totalSize += e.dataTransfer.files[i].size; }
|
||
if (totalSize > 1300000) { p5uploadFile(); return; } // File is too large, not sure what the real maximum is.
|
||
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('*'); // This will not work for large files, there is a limit on the data size in a field.
|
||
Q('p5fileDragLink').value = encodeURIComponent(filetreelinkpath);
|
||
Q('p5loginSubmit2').click();
|
||
}
|
||
}
|
||
reader.readAsDataURL(file);
|
||
}
|
||
}
|
||
}
|
||
|
||
var p5dragtimer = null;
|
||
function p5fileDragOver(e) {
|
||
if (xxdialogMode) return;
|
||
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) {
|
||
if (xxdialogMode) return;
|
||
haltEvent(e);
|
||
if (e.target.id != "p5filetable") {
|
||
QV('bigfail', false);
|
||
QV('bigok', false);
|
||
//QV('p5fileCatchAllInput', false);
|
||
} else {
|
||
p5dragtimer = setTimeout(function () { QV('bigfail',false); QV('bigok',false); p5dragtimer=null; }, 10);
|
||
}
|
||
}
|
||
|
||
/*
|
||
function p5fileCatchAllInputChanged(e) {
|
||
p5fileDragLeave(e);
|
||
Q('p5fileDragLink2').value = encodeURIComponent(filetreelinkpath);
|
||
Q('p5fileCatchAllSubmit').click();
|
||
}
|
||
*/
|
||
|
||
//
|
||
// MY EVENTS
|
||
//
|
||
|
||
// Highlights the device being hovered
|
||
function eventMouseHover(e, over) {
|
||
e.children[1].classList.remove('g1s');
|
||
e.children[2].style['background-color'] = ((over == 0) ? '#c9c9c9' : '#b9b9b9');
|
||
e.children[3].classList.remove('g2s');
|
||
if (over == 1) { e.children[1].classList.add('g1s'); e.children[3].classList.add('g2s'); }
|
||
}
|
||
|
||
function eventsUpdate() {
|
||
var x = '', dateHeader = null;
|
||
for (var i in events) {
|
||
var event = events[i], time = new Date(event.time);
|
||
if (event.msg) {
|
||
if (printDate(time) != dateHeader) {
|
||
if (dateHeader != null) x += '</table>';
|
||
dateHeader = printDate(time);
|
||
x += '<table class=p3eventsTable cellpadding=0 cellspacing=0><tr><td colspan=4 class=DevSt>' + dateHeader + '</td></tr>';
|
||
}
|
||
var icon = 'si3';
|
||
if (event.etype == 'user') icon = 'm2';
|
||
if (event.etype == 'server') icon = 'si3';
|
||
|
||
var msg = event.msg.split('(R)').join('®');
|
||
if (event.username && event.username != userinfo.name) { msg += ': ' + event.username; }
|
||
x += '<tr onmouseover=eventMouseHover(this,1) onmouseout=eventMouseHover(this,0) style=cursor:pointer><td style=width:18px><div class=' + icon + '></div></td><td class=g1> </td><td class=style10>' + printTime(time) + ' - ' + msg + '</td><td class=g2> </td></tr><tr style=height:2px></tr>';
|
||
}
|
||
}
|
||
if (dateHeader != null) x += '</table>';
|
||
if (x == '') x = "<br><i>No Events Found</i><br><br>";
|
||
QH('p3events', x);
|
||
}
|
||
|
||
function showDeleteAllEventsDialog() {
|
||
if (xxdialogMode) return;
|
||
var x = "Delete all events in the server event log?<br /><br />";
|
||
x += "<input id=p3check type=checkbox onchange=validateDeleteAllEventsDialog() />Confirm";
|
||
setDialogMode(2, "Delete All Events", 3, showDeleteAllEventsDialogEx, x);
|
||
validateDeleteAllEventsDialog();
|
||
}
|
||
|
||
function validateDeleteAllEventsDialog() {
|
||
QE('idx_dlgOkButton', Q('p3check').checked);
|
||
}
|
||
|
||
function showDeleteAllEventsDialogEx(buttons, tag) {
|
||
meshserver.send({ action: 'clearevents' });
|
||
}
|
||
|
||
function refreshEvents() {
|
||
meshserver.send({ action: 'events', limit: parseInt(p3limitdropdown.value) });
|
||
}
|
||
|
||
function p3showDownloadEventsDialog() {
|
||
if (xxdialogMode) return;
|
||
var x = 'Download the list of events with one of the file formats below.<br /><br />';
|
||
x += addHtmlValue('CSV Format', '<a style=cursor:pointer onclick=p3downloadEventsDialogCSV()>eventslist.csv</a>');
|
||
x += addHtmlValue('JSON Format', '<a style=cursor:pointer onclick=p3downloadEventsDialogJSON()>eventslist.json</a>');
|
||
setDialogMode(2, "Event List Export", 1, null, x);
|
||
}
|
||
|
||
function p3downloadEventsDialogCSV() {
|
||
var csv = "time, type, action, user, message\r\n";
|
||
for (var i in events) { csv += '\"' + events[i].time + '\",\"' + events[i].etype + '\",\"' + ((events[i].action != null)?events[i].action:'') + '\",\"' + ((events[i].username != null)?events[i].username:'') + '\",\"' + ((events[i].msg != null)?events[i].msg:'') + '\"\r\n'; }
|
||
saveAs(new Blob([csv], { type: "application/octet-stream" }), "eventslist.csv");
|
||
}
|
||
|
||
function p3downloadEventsDialogJSON() {
|
||
var r = []
|
||
for (var i in events) { r.push(events[i]); }
|
||
saveAs(new Blob([JSON.stringify(r)], { type: "application/octet-stream" }), "eventslist.json");
|
||
}
|
||
|
||
//
|
||
// MY USERS
|
||
//
|
||
|
||
function updateUsers() {
|
||
QV('MainMenuMyUsers', (users != null) && ((features & 4) == 0));
|
||
QV('LeftMenuMyUsers', (users != null) && ((features & 4) == 0));
|
||
QV('UserNewAccountButton', ((features & 4) == 0) && (serverinfo.domainauth == false));
|
||
if ((users == null) || ((features & 4) != 0)) { QH('p3users', ''); return; }
|
||
|
||
// Sort the list of user id's
|
||
var sortedUserIds = [], maxUsers = 100, hiddenUsers = 0;
|
||
for (var i in users) { sortedUserIds.push(i); }
|
||
sortedUserIds.sort();
|
||
|
||
// Get search
|
||
var userSearch = Q('UserSearchInput').value.toLowerCase();
|
||
var emailSearch = userSearch;
|
||
if (userSearch.startsWith('email:')) { userSearch = null; emailSearch = emailSearch.substring(6); }
|
||
else if (userSearch.startsWith('name:')) { emailSearch = null; userSearch = userSearch.substring(5); }
|
||
else if (userSearch.startsWith('e:')) { userSearch = null; emailSearch = emailSearch.substring(2); }
|
||
else if (userSearch.startsWith('n:')) { emailSearch = null; userSearch = userSearch.substring(2); }
|
||
|
||
// Display the users using the sorted list
|
||
var x = '<table class=p3usersTable cellpadding=0 cellspacing=0>', addHeader = true;
|
||
x += '<th>Name<th style=width:80px>Groups<th style=width:120px>Last Access<th style=width:120px>Permissions';
|
||
|
||
// Online users
|
||
for (var i in sortedUserIds) {
|
||
var user = users[sortedUserIds[i]], sessions = null;
|
||
if (wssessions != null) { sessions = wssessions[user._id]; }
|
||
if ((sessions != null) &&
|
||
((userSearch != null) && ((userSearch == '') || (user.name.toLowerCase().indexOf(userSearch) >= 0)) ||
|
||
((emailSearch != null) && ((user.email != null) && (user.email.toLowerCase().indexOf(emailSearch) >= 0))))
|
||
) {
|
||
if (maxUsers > 0) {
|
||
if (addHeader) { x += '<tr><td class=userTableHeader colspan=4>Online Users'; addHeader = false; }
|
||
x += addUserHtml(user, sessions);
|
||
maxUsers--;
|
||
} else {
|
||
hiddenUsers++;
|
||
}
|
||
}
|
||
}
|
||
addHeader = true;
|
||
// Offline users
|
||
for (var i in sortedUserIds) {
|
||
var user = users[sortedUserIds[i]], sessions = null;
|
||
if (wssessions != null) { sessions = wssessions[user._id]; }
|
||
if ((sessions == null) &&
|
||
((userSearch != null) && ((userSearch == '') || (user.name.toLowerCase().indexOf(userSearch) >= 0)) ||
|
||
((emailSearch != null) && ((user.email != null) && (user.email.toLowerCase().indexOf(emailSearch) >= 0))))
|
||
) {
|
||
if (maxUsers > 0) {
|
||
if (addHeader) { x += '<tr><td class=userTableHeader colspan=4>Offline Users'; addHeader = false; }
|
||
x += addUserHtml(user, sessions);
|
||
maxUsers--;
|
||
} else {
|
||
hiddenUsers++;
|
||
}
|
||
}
|
||
}
|
||
x += '</table>';
|
||
if (hiddenUsers == 1) { x += '<br />1 more user not shown, use search box to look for users...<br />'; }
|
||
else if (hiddenUsers > 1) { x += '<br />' + hiddenUsers + ' more users not shown, use search box to look for users...<br />'; }
|
||
if (maxUsers == 100) { x += '<br />No users found.<br />'; }
|
||
QH('p3users', x);
|
||
|
||
// Update current user panel if needed
|
||
if ((currentUser != null) && (xxcurrentView == 30)) { gotoUser(encodeURIComponent(currentUser._id),true); }
|
||
}
|
||
|
||
function addUserHtml(user, sessions) {
|
||
var x = '', gray = ' gray', icon = 'm2', msg = '', self = (user.name != userinfo.name), lastAccess = '', permissions = '';
|
||
if (sessions != null) {
|
||
gray = '';
|
||
if (self) {
|
||
msg = "<span style=float:right;margin-top:1px;margin-right:4px title=Chat><a onclick=userChat(event,\"" + encodeURIComponent(user._id) + "\",\"" + encodeURIComponent(user.name) + "\")><img src='images/icon-chat.png' height=16 width=16 style=padding-top:2px /></a></span>";
|
||
msg += "<span style=float:right;margin-top:1px;margin-left:4px;margin-right:4px title=Notify><a onclick=showUserAlertDialog(event,\"" + encodeURIComponent(user._id) + "\")><img src='images/icon-notify.png' height=16 width=16 style=padding-top:2px /></a></span>";
|
||
}
|
||
if (sessions == 1) { lastAccess += '1 session'; } else { lastAccess += sessions + ' sessions'; }
|
||
} else {
|
||
if (user.login) { lastAccess += '<span title="Last login: ' + printDateTime(new Date(user.login * 1000)) + '">' + printDate(new Date(user.login * 1000)) + '</span>'; }
|
||
}
|
||
if (self) { permissions += "<a style=cursor:pointer onclick=showUserAdminDialog(event,\"" + encodeURIComponent(user._id) + "\")>"; }
|
||
if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { permissions += "Locked, "; }
|
||
permissions += "<span title='Server Permissions'>";
|
||
|
||
var urights = user.siteadmin & (0xFFFFFFFF - 224);
|
||
if ((user.siteadmin == null) || (urights == 0)) {
|
||
permissions += "User";
|
||
} else if (urights == 8) {
|
||
permissions += "User + Files";
|
||
} else if (user.siteadmin == 0xFFFFFFFF) {
|
||
permissions += "Administrator";
|
||
} else if ((urights & 2) != 0) {
|
||
permissions += "Manager";
|
||
} else {
|
||
permissions += "Partial";
|
||
}
|
||
if ((user.siteadmin != null) && (user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & (64 + 128)) != 0)) { permissions += "*"; }
|
||
permissions += "</span>";
|
||
//if ((user.quota != null) && ((user.siteadmin & 8) != 0)) { msg += ", " + (user.quota / 1024) + " k"; }
|
||
if (self) { permissions += "</a>"; }
|
||
|
||
var groups = 0
|
||
if (user.links) { for (var i in user.links) { groups++; } }
|
||
|
||
var username = EscapeHtml(user.name), emailVerified = '';
|
||
if (serverinfo.emailcheck == true) { emailVerified = ((user.emailVerified != true) ? ' <b style=color:red title="Email is not verified">✗</b>' : ' <b style=color:green title="Email is verified">✓</b>'); }
|
||
if (user.email != null) { username += ', <a onclick=doemail(event,\"' + user.email + '\")>' + user.email + '</a>' + emailVerified; }
|
||
|
||
if ((user.otpsecret > 0) || (user.otphkeys > 0)) { username += ' <img src="images/key12.png" height=12 width=11 title="2nd factor authentication enabled" style="margin-top:2px" />'; }
|
||
if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { username += ' <img src="images/padlock12.png" height=12 width=8 title="Account is locked" style="margin-top:2px" />'; }
|
||
|
||
x += '<tr onmouseover=userMouseHover(this,1) onmouseout=userMouseHover(this,0)><td style=cursor:pointer onclick=gotoUser(\"' + encodeURIComponent(user._id) + '\")>';
|
||
x += '<div class=bar>';
|
||
x += '<div class=baricon><div class="' + icon + gray + '"></div></div>';
|
||
x += '<div class=g1></div><div class=g2></div>';
|
||
x += '<div><span>' + username + '</span>' + msg + '</div></div><td style=text-align:center>' + groups + '<td style=text-align:center>' + lastAccess + '<td style=text-align:center>' + permissions;
|
||
return x;
|
||
}
|
||
|
||
// Highlights the user being hovered
|
||
function userMouseHover(element, over) {
|
||
var e = element.children[0].children[0];
|
||
e.children[1].classList.remove('g1s');
|
||
e.children[2].classList.remove('g2s');
|
||
if (over == 1) { e.children[1].classList.add('g1s'); e.children[2].classList.add('g2s'); }
|
||
element.children[0].children[0].style['background-color'] = ((over == 0) ? '#c9c9c9' : '#b9b9b9');
|
||
}
|
||
|
||
function userChat(e, userid, name) {
|
||
haltEvent(e);
|
||
var url = '/messenger?id=meshmessenger/' + userid + '/' + encodeURIComponent(userinfo._id) + '&title=' + name;
|
||
if ((authCookie != null) && (authCookie != '')) { url += '&auth=' + authCookie; }
|
||
window.open(url, 'meshmessenger:' + userid);
|
||
meshserver.send({ action: 'meshmessenger', userid: decodeURIComponent(userid) });
|
||
return false;
|
||
}
|
||
|
||
function showUserAlertDialog(e, userid) {
|
||
if (xxdialogMode) return;
|
||
haltEvent(e);
|
||
setDialogMode(2, "Notify " + EscapeHtml(users[decodeURIComponent(userid)].name), 3, showUserAlertDialogEx, 'Send a text notification to this user.<textarea id=d2notifyText maxlength=2048 style="width:100%;height:184px;resize:none"></textarea>', userid);
|
||
Q('d2notifyText').focus();
|
||
return false;
|
||
}
|
||
|
||
function showUserAlertDialogEx(button, userid) { meshserver.send({ action: 'notifyuser', userid: decodeURIComponent(userid), msg: Q('d2notifyText').value }); }
|
||
|
||
function doemail(e, addr) {
|
||
if (xxdialogMode) return;
|
||
haltEvent(e);
|
||
window.open("mailto:" + addr);
|
||
return false;
|
||
}
|
||
|
||
function p4batchAccountCreate() {
|
||
if (xxdialogMode) return;
|
||
var x = 'Create many accounts at once by importing a JSON file with the following format:<br /><pre>[\r\n {"user":"x1","pass":"x","email":"x1@x"},\r\n {"user":"x2","pass":"x","resetNextLogin":true}\r\n]</pre><input style=width:370px type=file id=d4importFile accept=".json" onchange=p4batchAccountCreateValidate() />';
|
||
setDialogMode(2, "User Account Import", 3, p4batchAccountCreateEx, x);
|
||
QE('idx_dlgOkButton', false);
|
||
}
|
||
|
||
function p4batchAccountCreateValidate() {
|
||
QE('idx_dlgOkButton', Q('d4importFile').value != null);
|
||
}
|
||
|
||
function p4batchAccountCreateEx() {
|
||
var fr = new FileReader();
|
||
fr.onload = function (r) {
|
||
var j = null;
|
||
try { j = JSON.parse(r.target.result); } catch (ex) { setDialogMode(2, "User Account Import", 1, null, "Invalid JSON file: " + ex + "."); return; }
|
||
if ((j != null) && (Array.isArray(j))) {
|
||
var ok = true;
|
||
for (var i in j) {
|
||
if ((typeof j[i].user != 'string') || (j[i].user.length < 1) || (j[i].user.length > 64)) { ok = false; }
|
||
if ((typeof j[i].pass != 'string') || (j[i].pass.length < 1) || (j[i].pass.length > 256)) { ok = false; }
|
||
if (checkPasswordRequirements(j[i].pass, passRequirements) == false) { ok = false; }
|
||
if ((j[i].email != null) && ((typeof j[i].email != 'string') || (j[i].email.length < 1) || (j[i].email.length > 128))) { ok = false; }
|
||
}
|
||
if (ok == false) { setDialogMode(2, "User Account Import", 1, null, "Invalid JSON file format."); } else { meshserver.send({ action: 'adduserbatch', users: j }); }
|
||
} else { setDialogMode(2, "User Account Import", 1, null, "Invalid JSON file format."); }
|
||
};
|
||
fr.readAsText(Q('d4importFile').files[0]);
|
||
}
|
||
|
||
function p4downloadUserInfo() {
|
||
if (xxdialogMode) return;
|
||
var x = 'Download the list of users with one of the file formats below.<br /><br />';
|
||
x += addHtmlValue('CSV Format', '<a style=cursor:pointer onclick=p4downloadUserInfoCSV()>userlist.csv</a>');
|
||
x += addHtmlValue('JSON Format', '<a style=cursor:pointer onclick=p4downloadUserInfoJSON()>userlist.json</a>');
|
||
setDialogMode(2, "User List Export", 1, null, x);
|
||
}
|
||
|
||
function p4downloadUserInfoCSV() {
|
||
var csv = "id, name, email, creation, lastlogin, groups, authfactors\r\n";
|
||
for (var i in users) {
|
||
var multiFactor = false, factors = [];
|
||
if ((users[i].otpsecret > 0) || (users[i].otphkeys > 0)) {
|
||
multiFactor = true;
|
||
if (users[i].otpsecret > 0) { factors.push('AuthApp'); }
|
||
if (users[i].otphkeys > 0) { factors.push('SecurityKey'); }
|
||
if (users[i].otpkeys > 0) { factors.push('BackupCodes'); }
|
||
}
|
||
csv += '\"' + users[i]._id + '\",\"' + users[i].name + '\",\"' + (users[i].email ? users[i].email : '') + '\",\"' + (users[i].creation ? new Date(users[i].creation * 1000) : '') + '\",\"' + (users[i].login ? new Date(users[i].login * 1000) : '') + '\",\"' + (users[i].groups ? users[i].groups.join(',') : '') + '\",\"' + (multiFactor ? factors.join(',') : '') + '\"\r\n';
|
||
}
|
||
saveAs(new Blob([csv], { type: "application/octet-stream" }), "userlist.csv");
|
||
}
|
||
|
||
function p4downloadUserInfoJSON() {
|
||
var r = []
|
||
for (var i in users) { r.push(users[i]); }
|
||
saveAs(new Blob([JSON.stringify(r)], { type: "application/octet-stream" }), "userlist.json");
|
||
}
|
||
|
||
function showUserBroadcastDialog() {
|
||
if (xxdialogMode) return;
|
||
var x = 'Broadcast a message to all connected users.<textarea id=broadcastMessage value="" maxlength="256"/></textarea>';
|
||
setDialogMode(2, "Broadcast Message", 3, showUserBroadcastDialogEx, x);
|
||
Q('broadcastMessage').focus();
|
||
}
|
||
|
||
function showUserBroadcastDialogEx() {
|
||
meshserver.send({ action: 'userbroadcast', msg: Q('broadcastMessage').value });
|
||
}
|
||
|
||
function showCreateNewAccountDialog() {
|
||
if (xxdialogMode) return;
|
||
var x = '';
|
||
x += addHtmlValue('Name', '<input id=p4name maxlength=64 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
|
||
x += addHtmlValue('Email', '<input id=p4email maxlength=256 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
|
||
x += addHtmlValue('Password', '<input id=p4pass1 type=password maxlength=256 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
|
||
x += addHtmlValue('Password', '<input id=p4pass2 type=password maxlength=256 onchange=showCreateNewAccountDialogValidate() onkeyup=showCreateNewAccountDialogValidate() />');
|
||
x += '<div><input id=p4resetNextLogin type=checkbox />Force password reset on next login.</div>';
|
||
|
||
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 += '<div style=font-size:x-small;padding:6px>Requirements: ' + r.join(', ') + '.</div>'; }
|
||
}
|
||
|
||
setDialogMode(2, "Create Account", 3, showCreateNewAccountDialogEx, x);
|
||
showCreateNewAccountDialogValidate();
|
||
Q('p4name').focus();
|
||
}
|
||
|
||
function showCreateNewAccountDialogValidate(x) {
|
||
if ((x == null) && (Q('p4email').value.length > 0) && (validateEmail(Q('p4email').value)) == false) { QE('idx_dlgOkButton', false); return; }
|
||
var ok = (!Q('p4name') || ((Q('p4name').value.length > 0) && (Q('p4name').value.indexOf(' ') == -1))) && Q('p4pass1').value.length > 0 && Q('p4pass1').value == Q('p4pass2').value && checkPasswordRequirements(Q('p4pass1').value, passRequirements);
|
||
if (ok && passRequirements) { if (checkPasswordRequirements(Q('p4pass1').value, passRequirements) == false) { ok = false; } }
|
||
QE('idx_dlgOkButton', ok);
|
||
}
|
||
|
||
function showCreateNewAccountDialogEx() {
|
||
meshserver.send({ action: 'adduser', username: Q('p4name').value, email: Q('p4email').value, pass: Q('p4pass1').value, resetNextLogin: Q('p4resetNextLogin').checked });
|
||
}
|
||
|
||
function showUserGroupDialog(e, userid) {
|
||
if (xxdialogMode) return;
|
||
haltEvent(e);
|
||
userid = decodeURIComponent(userid);
|
||
var user = users[userid.toLowerCase()], groups = "";
|
||
if (user.groups != null) { groups = user.groups.join(', ') }
|
||
var x = 'Enter a comma seperate list of groups.<br /><br />';
|
||
x += addHtmlValue('Groups', '<input id=dp4usergroups style=width:230px value="' + groups + '" placeholder="Group1, Group2, Group3" maxlength=256 onchange=p4validateUserGroups() onkeyup=p4validateUserGroups() />');
|
||
setDialogMode(2, "User Groups", 3, showUserGroupDialogEx, x, user);
|
||
focusTextBox('dp4usergroups');
|
||
p4validateUserGroups();
|
||
return false;
|
||
}
|
||
|
||
function p4validateUserGroups() {
|
||
var groups = Q('dp4usergroups').value;
|
||
var k = 0, i = groups.indexOf('\"') + groups.indexOf('/') + groups.indexOf('>') + groups.indexOf('<') + groups.indexOf('\'');
|
||
var g = groups.split(',');
|
||
for (var j in g) { if (g[j].trim().length == 0) k++; }
|
||
QE('idx_dlgOkButton', (groups == '') || ((i == -5) && (k < 1)));
|
||
}
|
||
|
||
function showUserGroupDialogEx(event, user) {
|
||
var groups = Q('dp4usergroups').value, g = groups.split(','), g2 = [];
|
||
for (var j in g) { var x = g[j].trim(); if (x.length > 0) { g2.push(x); } }
|
||
meshserver.send({ action: 'edituser', id: user._id, groups: g2 });
|
||
}
|
||
|
||
function showUserAdminDialog(e, userid) {
|
||
if (xxdialogMode) return;
|
||
haltEvent(e);
|
||
userid = decodeURIComponent(userid);
|
||
var x = '<div><div id=d2AdminPermissions>';
|
||
x += '<input type=checkbox onchange=showUserAdminDialogValidate() id=ua_fileaccess>Server Files, <input type=number onchange=showUserAdminDialogValidate() maxlength=10 id=ua_fileaccessquota>k max, blank for default<br><hr/>';
|
||
x += '<input type=checkbox onchange=showUserAdminDialogValidate() id=ua_fulladmin>Full Administrator<br>';
|
||
x += '<input type=checkbox onchange=showUserAdminDialogValidate() id=ua_serverbackup>Server Backup<br>';
|
||
x += '<input type=checkbox onchange=showUserAdminDialogValidate() id=ua_serverrestore>Server Restore<br>';
|
||
x += '<input type=checkbox onchange=showUserAdminDialogValidate() id=ua_serverupdate>Server Updates<br>';
|
||
x += '<input type=checkbox onchange=showUserAdminDialogValidate() id=ua_manageusers>Manage Users<br>';
|
||
x += '<hr/></div><input type=checkbox onchange=showUserAdminDialogValidate() id=ua_lockedaccount>Lock Account<br>';
|
||
x += '<input type=checkbox onchange=showUserAdminDialogValidate() id=ua_nonewgroups>No New Device Groups<br>';
|
||
x += '<input type=checkbox onchange=showUserAdminDialogValidate() id=ua_nomeshcmd>No Tools (MeshCmd/Router)<br>';
|
||
x += '</div>';
|
||
var user = users[userid.toLowerCase()];
|
||
setDialogMode(2, "Server Permissions", 3, showUserAdminDialogEx, x, user);
|
||
if (user.siteadmin && user.siteadmin != 0) {
|
||
Q('ua_fulladmin').checked = (user.siteadmin == 0xFFFFFFFF);
|
||
Q('ua_serverbackup').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 1) != 0)); // Server Backup
|
||
Q('ua_manageusers').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 2) != 0)); // Manage Users
|
||
Q('ua_serverrestore').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 4) != 0)); // Server Restore
|
||
Q('ua_fileaccess').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 8) != 0)); // Server Files
|
||
Q('ua_serverupdate').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 16) != 0)); // Server Update
|
||
Q('ua_lockedaccount').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 32) != 0)); // Account locked
|
||
Q('ua_nonewgroups').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 64) != 0)); // No New Groups
|
||
Q('ua_nomeshcmd').checked = ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 128) != 0)); // No Tools (MeshCMD / Router)
|
||
}
|
||
QE('ua_fulladmin', userinfo.siteadmin == 0xFFFFFFFF);
|
||
QE('ua_serverbackup', userinfo.siteadmin == 0xFFFFFFFF);
|
||
QE('ua_manageusers', userinfo.siteadmin == 0xFFFFFFFF);
|
||
QE('ua_serverrestore', userinfo.siteadmin == 0xFFFFFFFF);
|
||
QE('ua_fileaccess', userinfo.siteadmin == 0xFFFFFFFF);
|
||
QE('ua_fileaccessquota', userinfo.siteadmin == 0xFFFFFFFF);
|
||
QE('ua_serverupdate', userinfo.siteadmin == 0xFFFFFFFF);
|
||
QV('d2AdminPermissions', userinfo.siteadmin == 0xFFFFFFFF)
|
||
QE('ua_lockedaccount', (userinfo.siteadmin & 2) && (user.siteadmin != 0xFFFFFFFF) && (userinfo._id != user._id));
|
||
QE('ua_nonewgroups', (userinfo.siteadmin & 2) && (user.siteadmin != 0xFFFFFFFF) && (userinfo._id != user._id));
|
||
QE('ua_nomeshcmd', (userinfo.siteadmin & 2) && (user.siteadmin != 0xFFFFFFFF) && (userinfo._id != user._id));
|
||
Q('ua_fileaccessquota').value = (user.quota != null)?(user.quota / 1024):'';
|
||
showUserAdminDialogValidate();
|
||
return false;
|
||
}
|
||
|
||
function showUserAdminDialogValidate() {
|
||
if (userinfo.siteadmin == 0xFFFFFFFF) {
|
||
QE('ua_serverbackup', !Q('ua_fulladmin').checked);
|
||
QE('ua_manageusers', !Q('ua_fulladmin').checked);
|
||
QE('ua_serverrestore', !Q('ua_fulladmin').checked);
|
||
QE('ua_fileaccess', !Q('ua_fulladmin').checked);
|
||
QE('ua_serverupdate', !Q('ua_fulladmin').checked);
|
||
QE('ua_lockedaccount', !Q('ua_fulladmin').checked);
|
||
QE('ua_nonewgroups', !Q('ua_fulladmin').checked);
|
||
QE('ua_nomeshcmd', !Q('ua_fulladmin').checked);
|
||
QE('ua_fileaccessquota', Q('ua_fileaccess').checked && !Q('ua_fulladmin').checked);
|
||
}
|
||
}
|
||
|
||
function showUserAdminDialogEx(button, user) {
|
||
var siteadmin = 0, quota = parseInt(Q('ua_fileaccessquota').value);
|
||
if (Q('ua_fulladmin').checked == true) { siteadmin = 0xFFFFFFFF; } else {
|
||
if (Q('ua_serverbackup').checked == true) siteadmin += 1;
|
||
if (Q('ua_manageusers').checked == true) siteadmin += 2;
|
||
if (Q('ua_serverrestore').checked == true) siteadmin += 4;
|
||
if (Q('ua_fileaccess').checked == true) siteadmin += 8;
|
||
if (Q('ua_serverupdate').checked == true) siteadmin += 16;
|
||
if (Q('ua_lockedaccount').checked == true) siteadmin += 32;
|
||
if (Q('ua_nonewgroups').checked == true) siteadmin += 64;
|
||
if (Q('ua_nomeshcmd').checked == true) siteadmin += 128;
|
||
}
|
||
var x = { action: 'edituser', id: user._id, siteadmin: siteadmin };
|
||
if (isNaN(quota) == false) { x.quota = (quota * 1024); }
|
||
meshserver.send(x);
|
||
}
|
||
|
||
function onUserSearchInputChanged() { updateUsers(); }
|
||
|
||
//
|
||
// MY USERS GENERAL
|
||
//
|
||
|
||
var currentUser = null;
|
||
function gotoUser(userid, force) {
|
||
if (xxdialogMode && !force) return;
|
||
var user = currentUser = users[decodeURIComponent(userid)];
|
||
if (user == null) { setDialogMode(0); go(4); return; }
|
||
QH('p30userName', user.name);
|
||
QH('p31userName', user.name);
|
||
var self = (user.name == userinfo.name), activeSessions = 0;
|
||
if (wssessions != null && wssessions[user._id]) { activeSessions = wssessions[user._id]; }
|
||
|
||
// Change user grayscale
|
||
Q('MainUserImage').classList.remove('gray');
|
||
if (activeSessions == 0) { Q('MainUserImage').classList.add('gray'); }
|
||
|
||
// Server permissions
|
||
var msg = [], premsg = '';
|
||
if ((user.siteadmin != null) && ((user.siteadmin & 32) != 0) && (user.siteadmin != 0xFFFFFFFF)) { premsg = '<img src="images/padlock12.png" height=12 width=8 title="Account is locked" style="margin-top:2px" /> '; msg.push("Locked account"); }
|
||
if ((user.siteadmin == null) || ((user.siteadmin & (0xFFFFFFFF - 224)) == 0)) { msg.push("No server rights"); } else if (user.siteadmin == 8) { msg.push("Access to server files"); } else if (user.siteadmin == 0xFFFFFFFF) { msg.push("Full administrator"); } else { msg.push("Partial rights"); }
|
||
if ((user.siteadmin != null) && (user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & (64 + 128)) != 0)) { msg.push("Restrictions"); }
|
||
|
||
// Show user attributes
|
||
var x = '<div style=min-height:80px><table style=width:100%>';
|
||
var email = user.email?EscapeHtml(user.email):'<i>Not set</i>', everify = '';
|
||
if (serverinfo.emailcheck) { everify = ((user.emailVerified == true) ? '<b style=color:green;cursor:pointer title="Email is verified">✓</b> ' : '<b style=color:red;cursor:pointer title="Email not verified">✗</b> '); }
|
||
if (user.name.toLowerCase() != user._id.split('/')[2]) { x += addDeviceAttribute('User Identifier', user._id.split('/')[2]); }
|
||
if ((user.siteadmin != 0xFFFFFFFF) || (userinfo.siteadmin == 0xFFFFFFFF)) { // If we are not site admin, we can't change a admin email.
|
||
x += addDeviceAttribute('Email', everify + "<a style=cursor:pointer onclick=p30showUserEmailChangeDialog(event,\"" + userid + "\")>" + email + '</a> <a style=cursor:pointer onclick=doemail(event,\"' + user.email + '\")><img class=hoverButton src="images/link1.png" /></a>');
|
||
} else {
|
||
x += addDeviceAttribute('Email', everify + email + ' <a style=cursor:pointer onclick=doemail(event,\"' + user.email + '\")><img class=hoverButton src="images/link1.png" /></a>');
|
||
}
|
||
x += addDeviceAttribute('Server Rights', premsg + "<a style=cursor:pointer onclick=showUserAdminDialog(event,\"" + userid + "\")>" + msg.join(', ') + "</a>");
|
||
if (user.quota) x += addDeviceAttribute('Server Quota', EscapeHtml(parseInt(user.quota) / 1024) + ' k');
|
||
x += addDeviceAttribute('Creation', printDateTime(new Date(user.creation * 1000)));
|
||
if (user.login) x += addDeviceAttribute('Last Login', printDateTime(new Date(user.login * 1000)));
|
||
if (user.passchange == -1) { x += addDeviceAttribute('Password', 'Will be changed on next login.'); }
|
||
else if (user.passchange) { x += addDeviceAttribute('Password', 'Last changed: ' + printDateTime(new Date(user.passchange * 1000))); }
|
||
|
||
// Device Groups
|
||
var linkCount = 0, linkCountStr = '<i>None<i>';
|
||
if (user.links) {
|
||
for (var i in user.links) { linkCount++; }
|
||
if (linkCount == 1) { linkCountStr = '1 group'; } else if (linkCount > 1) { linkCountStr = linkCount + ' groups'; }
|
||
}
|
||
x += addDeviceAttribute('Device Groups', linkCountStr);
|
||
|
||
// User Groups
|
||
var userGroups = '<i>None</i>';
|
||
if (user.groups) { userGroups = ''; for (var i in user.groups) { userGroups += '<span class="tagSpan">' + user.groups[i] + '</span>'; } }
|
||
x += addDeviceAttribute('User Groups', addLinkConditional(userGroups, 'showUserGroupDialog(event,\"' + userid + '\")', (userinfo.siteadmin == 0xFFFFFFFF) || ((userinfo.groups == null) && (userinfo.siteadmin & 2) && (userinfo._id != user._id) && (user._id != 0xFFFFFFFF))));
|
||
|
||
var multiFactor = 0;
|
||
if ((user.otpsecret > 0) || (user.otphkeys > 0)) {
|
||
multiFactor = 1;
|
||
var factors = [];
|
||
if (user.otpsecret > 0) { factors.push('Authentication App'); }
|
||
if (user.otphkeys > 0) { factors.push('Security Key'); }
|
||
if (user.otpkeys > 0) { factors.push('Backup Codes'); }
|
||
x += addDeviceAttribute('Security', '<img src="images/key12.png" height=12 width=11 title="2nd factor authentication enabled" style="margin-top:2px" /> ' + factors.join(', '));
|
||
}
|
||
|
||
x += '</table></div><br />';
|
||
|
||
// Add action buttons
|
||
x += '<input type=button value=Notes title="View notes about this user" onclick=showNotes(false,"' + userid + '") />';
|
||
if (!self && (activeSessions > 0)) { x += '<input type=button value=Notify title="Send user notification" onclick=showUserAlertDialog(event,"' + userid + '") />'; }
|
||
|
||
// Setup the panel
|
||
QH('p30html', x);
|
||
|
||
// Draw the user timeline
|
||
drawUserTimeline();
|
||
|
||
// Check if we can delete this user
|
||
var deletePossible = true;
|
||
if (user._id == userinfo._id) deletePossible = false;
|
||
if (user.siteadmin && user.siteadmin > 0 && userinfo.siteadmin != 0xFFFFFFFF) deletePossible = false;
|
||
|
||
// Show bottom buttons
|
||
x = '<div style=float:right;font-size:x-small>';
|
||
if (deletePossible) x += '<a style=cursor:pointer onclick=p30showDeleteUserDialog() title="Remove this user">Delete User</a>';
|
||
x += '</div><div style=font-size:x-small>';
|
||
if (userinfo.siteadmin == 0xFFFFFFFF) x += '<a style=cursor:pointer onclick=p30showUserChangePassDialog(' + multiFactor + ') title="Change the password for this user">Change Password</a>';
|
||
x += '</div><br>'
|
||
QH('p30html3', x);
|
||
|
||
// Update user's connection state
|
||
x = '';
|
||
if (activeSessions == 1) { x = '1 active session'; } else if (activeSessions > 1) { x = activeSessions + ' active sessions'; }
|
||
QH('MainUserState', x);
|
||
|
||
go(30);
|
||
|
||
// Update user events (TODO: do this only if we change users)
|
||
QH('p31events', '');
|
||
refreshUsersEvents();
|
||
}
|
||
|
||
// Display the user's email change dialog box
|
||
function p30showUserEmailChangeDialog(event) {
|
||
if (xxdialogMode) return;
|
||
var x = '';
|
||
x += addHtmlValue('Email', '<input id=dp30email style=width:230px maxlength=32 onchange=p30validateEmail() onkeyup=p30validateEmail() />');
|
||
if (serverinfo.emailcheck) { x += addHtmlValue('Status', '<select id=dp30verified style=width:230px onchange=p30validateEmail()><option value=0>Not verified</option><option value=1>Verified</option></select>'); }
|
||
setDialogMode(2, "Change Email for " + EscapeHtml(currentUser.name), 3, p30showUserEmailChangeDialogEx, x);
|
||
Q('dp30email').focus();
|
||
Q('dp30email').value = (currentUser.email?currentUser.email:'');
|
||
if (serverinfo.emailcheck) { Q('dp30verified').value = currentUser.emailVerified?1:0; }
|
||
p30validateEmail();
|
||
}
|
||
|
||
// Perform validation on the user's email change dialog box
|
||
function p30validateEmail() {
|
||
var v = Q('dp30email').value, x = v.split('@');
|
||
x = (x.length == 2) && (x[0].length > 0) && (x[1].split('.').length > 1) && (x[1].length > 2) && (v.length < 1024) && ((v != userinfo.email) || ((serverinfo.emailcheck == true) && (Q('dp30verified').value != (userinfo.emailVerified?1:0))));
|
||
QE('idx_dlgOkButton', x);
|
||
}
|
||
|
||
// Send to the server the new user's email address and validation status
|
||
function p30showUserEmailChangeDialogEx() {
|
||
var x = { action: 'edituser', id: currentUser._id, email: Q('dp30email').value };
|
||
if (serverinfo.emailcheck) { x.emailVerified = (Q('dp30verified').value == 1); }
|
||
meshserver.send(x);
|
||
}
|
||
|
||
// Display the user's password change dialog box
|
||
function p30showUserChangePassDialog(multiFactor) {
|
||
if (xxdialogMode) return;
|
||
var x = '';
|
||
x += addHtmlValue('Password', '<input id=p4pass1 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate(1) onkeyup=p30showUserChangePassDialogValidate(1)></input>');
|
||
x += addHtmlValue('Password', '<input id=p4pass2 type=password style=width:230px maxlength=256 onchange=showCreateNewAccountDialogValidate(1) onkeyup=p30showUserChangePassDialogValidate(1)></input>');
|
||
if (features & 0x00010000) { x += addHtmlValue('Password hint', '<input id=p4hint type=text style=width:230px maxlength=256></input>'); }
|
||
|
||
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 += '<div style=font-size:x-small;padding:6px>Requirements: ' + r.join(', ') + '.</div>'; }
|
||
}
|
||
|
||
x += '<div><input id=p4resetNextLogin type=checkbox />Force password reset on next login.</div>';
|
||
if (multiFactor == 1) { x += '<div><input id=p4twoFactorRemove type=checkbox />Remove all 2nd factor authentication.</div>'; }
|
||
setDialogMode(2, "Change Password for " + EscapeHtml(currentUser.name), 3, p30showUserChangePassDialogEx, x, multiFactor);
|
||
p30showUserChangePassDialogValidate();
|
||
Q('p4pass1').focus();
|
||
if (currentUser.passchange == -1) { Q('p4resetNextLogin').checked = true; }
|
||
}
|
||
|
||
function p30showUserChangePassDialogValidate() {
|
||
var ok = true;
|
||
if ((Q('p4pass1').value != '') || (Q('p4pass2').value != '')) {
|
||
if (Q('p4pass1').value != Q('p4pass2').value) { ok = false; } else {
|
||
if (passRequirements) { if (checkPasswordRequirements(Q('p4pass1').value, passRequirements) == false) { ok = false; } }
|
||
}
|
||
}
|
||
QE('idx_dlgOkButton', ok);
|
||
}
|
||
|
||
function p30showUserChangePassDialogEx(b, tag) {
|
||
var removeMultiFactor = false;
|
||
if ((tag == 1) && (Q('p4twoFactorRemove').checked == true)) { removeMultiFactor = true; }
|
||
if (Q('p4pass1').value == Q('p4pass2').value) {
|
||
var r = { action: 'changeuserpass', userid: currentUser._id, pass: Q('p4pass1').value, removeMultiFactor: removeMultiFactor, resetNextLogin: Q('p4resetNextLogin').checked };
|
||
if (features & 0x00010000) { r.hint = Q('p4hint').value; }
|
||
meshserver.send(r);
|
||
}
|
||
}
|
||
|
||
function p30showDeleteUserDialog() {
|
||
if (xxdialogMode) return;
|
||
setDialogMode(2, "Delete User " + EscapeHtml(currentUser.name), 3, p30showDeleteUserDialogEx, 'Confirm deletion of user ' + EscapeHtml(currentUser.name) + '?');
|
||
}
|
||
|
||
function p30showDeleteUserDialogEx() {
|
||
meshserver.send({ action: 'deleteuser', userid: currentUser._id, username: currentUser.name });
|
||
}
|
||
|
||
// Draw device power bars. The bars are 766px wide.
|
||
function drawUserTimeline() {
|
||
var timeline = null, now = Date.now();
|
||
//if (currentNode._id == powerTimelineNode) { timeline = powerTimeline; }
|
||
timeline = [];
|
||
|
||
// 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();
|
||
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) / 112794);
|
||
if (width > 0) {
|
||
var title = powerStateStrings2[block[2]] + ' from ' + printTime(new Date(ts)) + ' to ' + printTime(new Date(te)) + '.';
|
||
datavalue += '<div title="' + title + '" style=display:table-cell;width:' + width + 'px;background-color:' + powerColor(block[2]) + ';height:16px></div>';
|
||
}
|
||
}
|
||
}
|
||
x += '<tr style=' + (((count % 2) == 0)?'background-color:#DDD':'') + '><td><div> ' + printDate(date) + '<div></div></div></td><td><div>' + datavalue + '</div></td></tr>';
|
||
++count;
|
||
date = new Date(date.getTime() - (1000 * 60 * 60 * 24)); // Substract one day
|
||
}
|
||
QH('p30html2', '<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:center;width:150px>Day</th><th scope=col style=text-align:center>7 Day Login State</th></tr>' + x + '</tbody></table>');
|
||
}
|
||
|
||
//
|
||
// MY USERS EVENTS
|
||
//
|
||
|
||
var currentUserEvents = null;
|
||
function userEventsUpdate() {
|
||
var x = '', dateHeader = null;
|
||
for (var i in currentUserEvents) {
|
||
var event = currentUserEvents[i];
|
||
var time = new Date(event.time);
|
||
if (printDate(time) != dateHeader) {
|
||
if (dateHeader != null) x += '</table>';
|
||
dateHeader = printDate(time);
|
||
x += '<table style=width:100% cellpadding=0 cellspacing=0><tr><td class=DevSt>' + dateHeader + '</td></tr>';
|
||
}
|
||
var icon = 'si3';
|
||
if (event.etype == 'user') icon = 'm2';
|
||
if (event.etype == 'server') icon = 'si3';
|
||
|
||
var msg = event.msg.split('(R)').join('®');
|
||
if (event.username && event.username != userinfo.name) { msg += ': ' + event.username; }
|
||
x += '<tr><td><div class=bar18 style=height:18px;width:100%;font-size:medium>';
|
||
x += '<div style=float:left;height:18px;width:18px;background-color:white><div class=' + icon + ' style=width:16px;margin-top:1px;margin-left:2px;height:16px></div></div>';
|
||
x += '<div class=g1 style=height:18px;float:left></div><div class=g2 style=height:18px;float:right></div>';
|
||
x += '<div style=font-size:14px><span style=width:300px>' + printTime(time) + ' - ' + msg + '</span></div></div></td></tr>';
|
||
}
|
||
if (dateHeader != null) x += '</table>';
|
||
if (x == '') x = "<br><i>No Events Found</i><br><br>";
|
||
QH('p31events', x);
|
||
}
|
||
|
||
function refreshUsersEvents() {
|
||
meshserver.send({ action: 'events', limit: parseInt(p31limitdropdown.value), user: currentUser.name });
|
||
}
|
||
|
||
//
|
||
// FILE SELECTOR, DIALOG 3
|
||
//
|
||
|
||
function d3init() {
|
||
Q('d3localFile').value = '';
|
||
d3modechange();
|
||
}
|
||
|
||
function d3modechange() {
|
||
var mode = Q('d3uploadMode').value;
|
||
QV('d3localmode', mode == 1);
|
||
QV('d3servermode', mode == 2);
|
||
if (mode == 1) { d3setActions(); } else { d3updatefiles(); }
|
||
}
|
||
|
||
var d3filetreelinkpath;
|
||
var d3filetreelocation = [];
|
||
|
||
function d3updatefiles() {
|
||
if (Q('d3uploadMode').value == 1) return;
|
||
var html1 = '', html2 = '', filetreex = filetree, folderdepth = 1;
|
||
|
||
// Navigate to path location, build the paths at the same time
|
||
var d3filetreelocation2 = [], oldlinkpath = d3filetreelinkpath, 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
|
||
|
||
d3filetreelinkpath = '';
|
||
for (var i in d3filetreelocation) {
|
||
if ((filetreex.f != null) && (filetreex.f[d3filetreelocation[i]] != null)) {
|
||
d3filetreelocation2.push(d3filetreelocation[i]);
|
||
if ((folderdepth == 1)) {
|
||
var sp = d3filetreelocation[i].split('/');
|
||
publicPath = window.location + sp[0] + 'files/' + sp[2];
|
||
if (d3filetreelocation[i] === userinfo._id) { d3filetreelinkpath += 'self'; } else { d3filetreelinkpath += (sp[0] + '/' + sp[2]); }
|
||
} else {
|
||
if (d3filetreelinkpath != '') { d3filetreelinkpath += '/' + d3filetreelocation[i]; if (folderdepth > 2) { publicPath += '/' + d3filetreelocation[i]; } }
|
||
}
|
||
filetreex = filetreex.f[d3filetreelocation[i]];
|
||
folderdepth++;
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
d3filetreelocation = d3filetreelocation2; // In case we could not go down the full path, we set the new path location here.
|
||
|
||
// 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 > 70) { shortname = '<span title="' + EscapeHtml(name) + '">' + EscapeHtml(name.substring(0, 70)) + "...</span>"; } 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 title = '';
|
||
h = "<div class=filelist file=999><span style=float:right title=\"" + title + "\"></span><span><div class=fileIcon" + f.t + " onclick=d3folderset(\"" + encodeURIComponent(f.nx) + "\")></div> <a style=cursor:pointer onclick=d3folderset(\"" + encodeURIComponent(f.nx) + "\")>" + shortname + "</a></span></div>";
|
||
} else {
|
||
var link = shortname;
|
||
//if (f.s > 0) { link = "<a rel=\"noreferrer noopener\" target=\"_blank\" href=\"downloadfile.ashx?link=" + encodeURIComponent(filetreelinkpath + '/' + f.nx) + "\">" + shortname + "</a>"; }
|
||
h = "<div class=filelist file=3><input style=float:left name=fcx class=fcb type=checkbox onchange=d3setActions() value='" + f.nx + "'> <span style=float:right>" + fsize + "</span><span><div class=fileIcon" + f.t + "></div>" + link + "</span></div>";
|
||
}
|
||
|
||
if (f.t < 3) { html1 += h; } else { html2 += h; }
|
||
}
|
||
|
||
QH('d3serverfiles', html1 + html2);
|
||
QE('p3FolderUp', d3filetreelocation.length > 0);
|
||
d3setActions();
|
||
}
|
||
|
||
function d3folderset(x) { d3filetreelocation.push(decodeURIComponent(x)); d3updatefiles(); }
|
||
function d3folderup(x) { if (x == null) { d3filetreelocation.pop(); } else { while (d3filetreelocation.length > x) { d3filetreelocation.pop(); } } d3updatefiles(); }
|
||
function d3getFileSel() { var cc = []; var checkboxes = document.getElementsByName('fcx'); for (var i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { cc.push(checkboxes[i].value) } } return cc; }
|
||
function d3setActions() {
|
||
var mode = Q('d3uploadMode').value;
|
||
if (mode == 1) {
|
||
QE('idx_dlgOkButton', Q('d3localFile').value.length > 0);
|
||
} else {
|
||
QE('idx_dlgOkButton', d3getFileSel().length == 1);
|
||
}
|
||
}
|
||
|
||
//
|
||
// NOTIFICATIONS
|
||
//
|
||
|
||
var notifications = [];
|
||
|
||
// Toggle showing notifications
|
||
function clickNotificationIcon(show) {
|
||
//addNotification({ icon:0, text:'test' });
|
||
if (show == true) { QV('notifiyBox', true); } else if (show == false) { QV('notifiyBox', false); } else { QV('notifiyBox', QS('notifiyBox')['display'] == 'none'); }
|
||
drawNotifications();
|
||
}
|
||
|
||
// Set the notification count on the upper right oft he screen
|
||
function setNotificationCount(c) {
|
||
if (parseInt(Q('notificationCount').innerHTML) == c) return; // If the count did not change, exit now.
|
||
QH('notificationCount', c);
|
||
QS('notificationCount')['background-color'] = (c == 0)?'lightblue':'orange';
|
||
QV('notificationCount', c > 0);
|
||
}
|
||
|
||
// Refresh the notification box
|
||
function drawNotifications() {
|
||
var r = '';
|
||
if (notifications.length == 0) {
|
||
r = '<div style=margin:5px>There are currently no notifications</div>';
|
||
} else {
|
||
for (var i in notifications) {
|
||
var n = notifications[i];
|
||
var t = '';
|
||
if (n.title != null) { t = '<b>' + n.title + '</b>: ' }
|
||
var d = new Date(n.time);
|
||
var icon = 0;
|
||
if (n.nodeid != null) {
|
||
var node = getNodeFromId(n.nodeid);
|
||
if (node != null) { icon = node.icon; t = '<b>' + node.name + '</b>: ' }
|
||
}
|
||
|
||
r += '<div title="Occured at ' + printDateTime(d) + '" id="notifyx' + n.id + '" class=notification style="cursor:pointer;border-top:1px solid ' + ((r == '') ? 'transparent' : 'orange') + '">';
|
||
if (icon) { r += '<div class=j' + icon + ' onclick="notificationSelected(' + n.id + ')" style=margin:5px;float:left></div>'; }
|
||
r += '<div onclick="notificationDelete(' + n.id + ')" class=unselectable title="Clear this notification" style=margin:5px;float:right;color:orange><b>X</b></div><div onclick="notificationSelected(' + n.id + ')" style=margin:5px>' + t + n.text + '</div></div>';
|
||
}
|
||
}
|
||
var deleteall = '';
|
||
if (notifications.length > 1) { deleteall = '<div id="notifyRemoveAll" onclick="deleteAllNotifications()" style="cursor:pointer;border-top:1px solid orange;margin:5px;color:orange;text-align:right;padding-right:3px">Clear all</div>'; }
|
||
QH('notifiyBox', '<div class=customScroll style="max-height:170px;overflow-y:auto;margin:5px">' + r + '</div>' + deleteall );
|
||
}
|
||
|
||
// A notification was selected
|
||
function notificationSelected(id, del) {
|
||
var j = -1;
|
||
for (var i in notifications) { if (notifications[i].id == id) { j = i; } }
|
||
if (j != -1) {
|
||
notificationSelectedEx(notifications[j], id);
|
||
if (del && notifications[j]) {
|
||
if (notifications[j].notification) { notifications[j].notification.close(); delete notifications[j].notification; }
|
||
notificationDelete(id);
|
||
}
|
||
}
|
||
}
|
||
|
||
function notificationSelectedEx(n, id) {
|
||
if (n.nodeid != null) {
|
||
if (n.tag == 'desktop') gotoDevice(n.nodeid, 12); // Desktop
|
||
else if (n.tag == 'terminal') gotoDevice(n.nodeid, 11); // Terminal
|
||
else if (n.tag == 'files') gotoDevice(n.nodeid, 13); // Files
|
||
else if (n.tag == 'intelamt') gotoDevice(n.nodeid, 14); // Intel AMT
|
||
else if (n.tag == 'console') gotoDevice(n.nodeid, 15); // Files
|
||
else gotoDevice(n.nodeid, 10); // General
|
||
} else {
|
||
if ((n.tag != null) && n.tag.startsWith('meshmessenger/')) {
|
||
window.open('/messenger?id=' + n.tag + '&title=' + encodeURIComponent(n.username), n.tag.split('/')[2]);
|
||
notificationDelete(id);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Remove one notification
|
||
function notificationDelete(id) {
|
||
var j = -1, e = Q('notifyx' + id);
|
||
if (e != null) {
|
||
for (var i in notifications) { if (notifications[i].id == id) { j = i; } }
|
||
if (j != -1) {
|
||
if (notifications[j].notification) { notifications[j].notification.close(); delete notifications[j].notification; }
|
||
notifications.splice(j, 1);
|
||
e.parentNode.removeChild(e);
|
||
setNotificationCount(notifications.length);
|
||
if (notifications.length == 0) { QV('notifiyBox', false); }
|
||
if (notifications.length == 1) { QV('notifyRemoveAll', false); }
|
||
if ((notifications.length > 0) && (j == 0)) {
|
||
var n = notifications[0];
|
||
QS('notifyx' + n.id)['border-top'] = '1px solid transparent';
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Add a new notification and play the notification sound
|
||
function addNotification(n) {
|
||
// Show notification within the web page.
|
||
if (n.time == null) { n.time = Date.now(); }
|
||
if (n.id == null) { n.id = Math.random(); }
|
||
notifications.unshift(n);
|
||
setNotificationCount(notifications.length);
|
||
clickNotificationIcon(true);
|
||
Q('chimes').play();
|
||
|
||
// If web notifications are granted, use it.
|
||
var notification = null;
|
||
if (Notification && (Notification.permission == "granted")) {
|
||
var text = n.text.split('®').join('').split('<b>').join('').split('</b>').join('').split('<br />').join('\r\n'); // Clean up any HTML codes
|
||
if (n.nodeid) {
|
||
var node = getNodeFromId(n.nodeid);
|
||
if (node) { notification = new Notification("{{{title}}} - " + node.name, { tag: n.tag, body: text, icon: '/images/notify/icons128-' + node.icon + '.png' }); }
|
||
} else {
|
||
if (n.icon == null) { n.icon = 0; }
|
||
var title = n.title;
|
||
if (title == null) { title = ''; } else { title = ' - ' + n.title; }
|
||
notification = new Notification("{{{title}}}" + title, { tag: n.tag, body: text, icon: '/images/notify/icons128-' + n.icon + '.png' });
|
||
}
|
||
notification.id = n.id;
|
||
notification.xtag = n.tag;
|
||
notification.nodeid = n.nodeid;
|
||
notification.username = n.username;
|
||
notification.onclick = function (e) { notificationSelected(e.target.id, true); }
|
||
n.notification = notification;
|
||
}
|
||
}
|
||
|
||
// Remove all notifications
|
||
function deleteAllNotifications() {
|
||
notifications = [];
|
||
setNotificationCount(0);
|
||
drawNotifications();
|
||
QV('notifiyBox', false);
|
||
}
|
||
|
||
//
|
||
// MyServer General
|
||
//
|
||
|
||
function setupGeneralServerStats() {
|
||
window.serverStatCpu = new Chart(document.getElementById('serverCpuChart').getContext('2d'), {
|
||
type: 'doughnut',
|
||
data: { datasets: [{ data: [0, 0], backgroundColor: ['#AAAAAA', '#00AA00'] }], labels: ['Used', 'Free'] },
|
||
options: { responsive: true, legend: { position: 'none', }, animation: { animateScale: true, animateRotate: true }, width: '60px' }
|
||
});
|
||
window.serverStatMemory = new Chart(document.getElementById('serverMemoryChart').getContext('2d'), {
|
||
type: 'doughnut',
|
||
data: { datasets: [{ data: [0, 0], backgroundColor: ['#AAAAAA', '#00AA00'] }], labels: ['Used', 'Free'] },
|
||
options: { responsive: true, legend: { position: 'none', }, animation: { animateScale: true, animateRotate: true }, width: '60px' }
|
||
});
|
||
}
|
||
|
||
var lastServerStats = null;
|
||
function updateGeneralServerStats(message) {
|
||
if (message != null) { lastServerStats = message; } else { message = lastServerStats; }
|
||
if (message == null) return;
|
||
|
||
// Paint the pie graphs
|
||
if (typeof message.cpuavg == 'object') {
|
||
var m = Math.min(message.cpuavg[0], 1);
|
||
window.serverStatCpu.config.data.datasets[0].data = [m, 1 - m];
|
||
QH('serverCpuChartText', '<div style=margin-bottom:5px>CPU Load</div><div><b title="CPU load in the last minute">' + (Math.round(message.cpuavg[0] * 100.0) / 100.0) + '</b>, <b title="CPU load in the last 5 minutes">' + (Math.round(message.cpuavg[1] * 100.0) / 100.0) + '</b>, <b title="CPU load in the 15 minutes">' + (Math.round(message.cpuavg[2] * 100.0) / 100.0) + '</b></div>');
|
||
QS('serverCpuChartView')['display'] = 'inline-block';
|
||
window.serverStatCpu.update();
|
||
}
|
||
if ((typeof message.totalmem == 'number') && (typeof message.freemem == 'number')) {
|
||
window.serverStatMemory.config.data.datasets[0].data = [message.totalmem - message.freemem, message.freemem];
|
||
QH('serverMemoryChartText', '<div style=margin-bottom:5px>Memory</div><div><b>' + getNiceSize2(message.freemem) + '</b> free, <b>' + getNiceSize2(message.totalmem) + '</b> total</div>');
|
||
QS('serverMemoryChartView')['display'] = 'inline-block';
|
||
window.serverStatMemory.update();
|
||
}
|
||
|
||
// Display all of the server values
|
||
var x = '<div style=width:100% cellpadding=0 cellspacing=0>';
|
||
if (typeof message.values == 'object') {
|
||
for (var i in message.values) {
|
||
x += '<div class=userTableHeader style=margin-bottom:4px;width:200px>' + i + '</div>';
|
||
for (var j in message.values[i]) {
|
||
x += '<div style=display:inline-block><table class=serverStateTableCell><tr><td class=h1></td><td><span>' + j + '</span><span style=float:right>' + message.values[i][j] + '</span></td><td class=h2></td></tr></table></div>';
|
||
}
|
||
}
|
||
}
|
||
x += '</div>';
|
||
|
||
QH('serverStatsTable', x);
|
||
}
|
||
|
||
//
|
||
// MyServer Stats
|
||
//
|
||
|
||
var serverTimelineStats = null;
|
||
var serverTimelineConfig = {
|
||
type: 'line',
|
||
data: { labels: [], datasets: [{ label: '', backgroundColor: 'rgba(255, 99, 132, .5)', borderColor: 'rgb(255, 99, 132)', data: [], fill: true }] },
|
||
options: {
|
||
responsive: true,
|
||
maintainAspectRatio: false,
|
||
scales: {
|
||
xAxes: [{ type: 'time', time: { tooltipFormat: 'll HH:mm' }, display: true, scaleLabel: { display: false, labelString: '' } }],
|
||
yAxes: [{ type: 'linear', display: true, scaleLabel: { display: true, labelString: '' } }]
|
||
}
|
||
}
|
||
};
|
||
|
||
function refreshServerTimelineStats(stats) { meshserver.send({ action: 'servertimelinestats', hours: 24 * 30 }); }
|
||
function pastDate(hours) { var t = new Date(); t.setTime(t.getTime() - (60 * 60 * 1000 * hours)); return t; }
|
||
function setServerTimelineStats(stats) { serverTimelineStats = stats; updateServerTimelineStats(); }
|
||
function addServerTimelineStats(stats) {
|
||
if (serverTimelineStats == null) return;
|
||
serverTimelineStats.push(stats);
|
||
var chartType = Q('p40type').value;
|
||
if (chartType == 0) {
|
||
serverTimelineConfig.data.datasets[0].data.push({ x: stats.time, y: stats.conn.ca });
|
||
serverTimelineConfig.data.datasets[1].data.push({ x: stats.time, y: stats.conn.cu });
|
||
serverTimelineConfig.data.datasets[2].data.push({ x: stats.time, y: stats.conn.us });
|
||
serverTimelineConfig.data.datasets[3].data.push({ x: stats.time, y: stats.conn.rs });
|
||
if (stats.conn.am != null) { serverTimelineConfig.data.datasets[4].data.push({ x: stats.time, y: stats.conn.am }); }
|
||
} else if (chartType == 1) {
|
||
serverTimelineConfig.data.datasets[0].data.push({ x: stats.time, y: stats.mem.external / (1024 * 1024) });
|
||
serverTimelineConfig.data.datasets[1].data.push({ x: stats.time, y: stats.mem.heapUsed / (1024 * 1024) });
|
||
serverTimelineConfig.data.datasets[2].data.push({ x: stats.time, y: stats.mem.heapTotal / (1024 * 1024) });
|
||
serverTimelineConfig.data.datasets[3].data.push({ x: stats.time, y: stats.mem.rss / (1024 * 1024) });
|
||
} /* else if (chartType == 2) {
|
||
serverTimelineConfig.data.datasets[0].data.push({ x: stats.time, y: stats.db.meshes });
|
||
serverTimelineConfig.data.datasets[1].data.push({ x: stats.time, y: stats.db.nodes });
|
||
serverTimelineConfig.data.datasets[2].data.push({ x: stats.time, y: stats.db.users });
|
||
serverTimelineConfig.data.datasets[3].data.push({ x: stats.time, y: stats.db.total });
|
||
} */
|
||
updateServerTimelineHours();
|
||
}
|
||
function updateServerTimelineHours() {
|
||
serverTimelineConfig.options.scales.yAxes[0].type = (Q('p40log').checked ? 'logarithmic' : 'linear');
|
||
serverTimelineConfig.options.scales.xAxes[0].time = { min: pastDate(Q('p40time').value) };
|
||
window.serverMainStats.update();
|
||
}
|
||
function setupServerTimelineStats() { window.serverMainStats = new Chart(document.getElementById('serverMainStats').getContext('2d'), serverTimelineConfig); }
|
||
|
||
function updateServerTimelineStats() {
|
||
var data, chartType = Q('p40type').value, timeAfter = pastDate(Q('p40time').value);
|
||
serverTimelineConfig.options.scales.xAxes[0].time = { min: timeAfter };
|
||
if (chartType == 0) { // Connections
|
||
serverTimelineConfig.options.scales.yAxes[0].scaleLabel.labelString = 'Connection Count';
|
||
data = {
|
||
labels: [pastDate(0), timeAfter],
|
||
datasets: [
|
||
{ label: 'Agents', data: [], backgroundColor: 'rgba(158, 151, 16, .1)', borderColor: 'rgb(158, 151, 16)', fill: true },
|
||
{ label: 'Users', data: [], backgroundColor: 'rgba(16, 84, 158, .1)', borderColor: 'rgb(16, 84, 158)', fill: true },
|
||
{ label: 'User Sessions', data: [], backgroundColor: 'rgba(255, 99, 132, .1)', borderColor: 'rgb(255, 99, 132)', fill: true },
|
||
{ label: 'Relay Sessions', data: [], backgroundColor: 'rgba(39, 158, 16, .1)', borderColor: 'rgb(39, 158, 16)', fill: true },
|
||
{ label: 'Intel AMT', data: [], backgroundColor: 'rgba(134, 16, 158, .1)', borderColor: 'rgb(134, 16, 158)', fill: true }
|
||
]
|
||
};
|
||
for (var i = 0; i < serverTimelineStats.length; i++) {
|
||
var t = new Date(serverTimelineStats[i].time);
|
||
if (serverTimelineStats[i].conn) {
|
||
data.datasets[0].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].conn.ca });
|
||
data.datasets[1].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].conn.cu });
|
||
data.datasets[2].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].conn.us });
|
||
data.datasets[3].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].conn.rs });
|
||
if (serverTimelineStats[i].conn.am != null) { data.datasets[4].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].conn.am }); }
|
||
}
|
||
}
|
||
} else if (chartType == 1) { // Memory
|
||
serverTimelineConfig.options.scales.yAxes[0].scaleLabel.labelString = 'Megabytes';
|
||
data = {
|
||
labels: [pastDate(0), timeAfter],
|
||
datasets: [
|
||
{ label: 'External', data: [], backgroundColor: 'rgba(158, 151, 16, .1)', borderColor: 'rgb(158, 151, 16)', fill: true },
|
||
{ label: 'Heap Used', data: [], backgroundColor: 'rgba(16, 84, 158, .1)', borderColor: 'rgb(16, 84, 158)', fill: true },
|
||
{ label: 'Heap Total', data: [], backgroundColor: 'rgba(255, 99, 132, .1)', borderColor: 'rgb(255, 99, 132)', fill: true },
|
||
{ label: 'RSS', data: [], backgroundColor: 'rgba(39, 158, 16, .1)', borderColor: 'rgb(39, 158, 16)', fill: true }
|
||
]
|
||
};
|
||
for (var i = 0; i < serverTimelineStats.length; i++) {
|
||
data.datasets[0].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].mem.external / (1024 * 1024) });
|
||
data.datasets[1].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].mem.heapUsed / (1024 * 1024) });
|
||
data.datasets[2].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].mem.heapTotal / (1024 * 1024) });
|
||
data.datasets[3].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].mem.rss / (1024 * 1024) });
|
||
}
|
||
} /*else if (chartType == 2) { // Database
|
||
serverTimelineConfig.options.scales.yAxes[0].scaleLabel.labelString = 'Records';
|
||
data = {
|
||
labels: [pastDate(0), timeAfter],
|
||
datasets: [
|
||
{ label: 'Groups', data: [], backgroundColor: 'rgba(158, 151, 16, .1)', borderColor: 'rgb(158, 151, 16)', fill: true },
|
||
{ label: 'Devices', data: [], backgroundColor: 'rgba(16, 84, 158, .1)', borderColor: 'rgb(16, 84, 158)', fill: true },
|
||
{ label: 'Users', data: [], backgroundColor: 'rgba(255, 99, 132, .1)', borderColor: 'rgb(255, 99, 132)', fill: true },
|
||
{ label: 'Records', data: [], backgroundColor: 'rgba(39, 158, 16, .1)', borderColor: 'rgb(39, 158, 16)', fill: true }
|
||
]
|
||
};
|
||
for (var i = 0; i < serverTimelineStats.length; i++) {
|
||
data.datasets[0].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].db.meshes });
|
||
data.datasets[1].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].db.nodes });
|
||
data.datasets[2].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].db.users });
|
||
data.datasets[3].data.push({ x: serverTimelineStats[i].time, y: serverTimelineStats[i].db.total });
|
||
}
|
||
}*/
|
||
serverTimelineConfig.data = data;
|
||
window.serverMainStats.update();
|
||
}
|
||
|
||
function p40downloadEvents() {
|
||
var csv = "time, conn.agent, conn.users, conn.usersessions, conn.relaysession, conn.intelamt, mem.external, mem.heapused, mem.heaptotal, mem.rss\r\n";
|
||
for (var i = 0; i < serverTimelineStats.length; i++) {
|
||
if (serverTimelineStats[i].conn && serverTimelineStats[i].mem) {
|
||
csv += new Date(serverTimelineStats[i].time) + ', ' + serverTimelineStats[i].conn.ca + ', ' + serverTimelineStats[i].conn.cu + ', ' + serverTimelineStats[i].conn.us + ', ' + serverTimelineStats[i].conn.rs + ', ' + (serverTimelineStats[i].conn.am ? serverTimelineStats[i].conn.am : '') + ', ' + serverTimelineStats[i].mem.external + ', ' + serverTimelineStats[i].mem.heapUsed + ', ' + serverTimelineStats[i].mem.heapTotal + ', ' + serverTimelineStats[i].mem.rss + '\r\n';
|
||
}
|
||
}
|
||
saveAs(new Blob([csv], { type: "application/octet-stream" }), "ServerStats.csv");
|
||
}
|
||
|
||
//
|
||
// POPUP DIALOG
|
||
//
|
||
|
||
// null = Hidden, 1 = Generic Message
|
||
var xxdialogMode;
|
||
var xxdialogFunc;
|
||
var xxdialogButtons;
|
||
var xxdialogTag;
|
||
var xxcurrentView = -1;
|
||
|
||
// 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();
|
||
QV('uiMenu', false);
|
||
xxdialogMode = x;
|
||
xxdialogFunc = f;
|
||
xxdialogButtons = b;
|
||
xxdialogTag = tag;
|
||
QE('idx_dlgOkButton', true);
|
||
QV('idx_dlgOkButton', b & 1);
|
||
QV('idx_dlgCancelButton', b & 2);
|
||
QV('id_dialogclose', (b & 2) || (b & 8));
|
||
QV('idx_dlgDeleteButton', b & 4);
|
||
QV('idx_dlgButtonBar', b & 7);
|
||
if (y) QH('id_dialogtitle', y);
|
||
for (var i = 1; i < 24; i++) { QV('dialog' + i, i == x); } // Edit this line when more dialogs are added
|
||
QV('dialog', x);
|
||
if (c) { if (x == 2) { QH('id_dialogOptions', c); } else { QH('id_dialogMessage', c); } }
|
||
}
|
||
|
||
function dialogclose(x) {
|
||
setSessionActivity();
|
||
var f = xxdialogFunc, b = xxdialogButtons, t = xxdialogTag;
|
||
setDialogMode();
|
||
if (((b & 8) || x) && f) f(x, t);
|
||
}
|
||
|
||
function center() {
|
||
setSessionActivity();
|
||
if (xxcurrentView == 11) { deskAdjust(); }
|
||
else if (xxcurrentView == 10) { masterUpdate(256); }
|
||
else if (xxcurrentView == 1) { masterUpdate(4); }
|
||
}
|
||
function messagebox(t, m) { setSessionActivity(); QH('id_dialogMessage', m); setDialogMode(1, t, 1); }
|
||
function statusbox(t, m) { setSessionActivity(); QH('id_dialogMessage', m); setDialogMode(1, t); }
|
||
|
||
function goBack() {
|
||
setSessionActivity();
|
||
if (xxdialogMode) return;
|
||
if (fullscreen) { deskToggleFull(); }
|
||
if ((xxcurrentView >= 10) && (xxcurrentView < 20)) { go(1); } // Return to My Devices
|
||
if ((xxcurrentView >= 20) && (xxcurrentView < 30)) { go(2); } // Return to My Account
|
||
if ((xxcurrentView >= 30) && (xxcurrentView < 40)) { go(4); } // Return to My Users
|
||
}
|
||
|
||
function go(x) {
|
||
setSessionActivity();
|
||
if (xxdialogMode || xxcurrentView == x) return;
|
||
QV('uiMenu', false);
|
||
|
||
// Edit this line when adding a new screen
|
||
for (var i = 0; i < 41; i++) { QV('p' + i, i == x); }
|
||
xxcurrentView = x;
|
||
|
||
// Remove top bar selection
|
||
var mainBarItems = ['MainMenuMyDevices', 'MainMenuMyAccount', 'MainMenuMyEvents', 'MainMenuMyFiles', 'MainMenuMyUsers', 'MainMenuMyServer'];
|
||
for (var i in mainBarItems) {
|
||
QC(mainBarItems[i]).remove('fullselect');
|
||
QC(mainBarItems[i]).remove('semiselect');
|
||
}
|
||
|
||
// Remove left bar selection
|
||
var leftBarItems = ['LeftMenuMyDevices', 'LeftMenuMyAccount', 'LeftMenuMyEvents', 'LeftMenuMyFiles', 'LeftMenuMyUsers', 'LeftMenuMyServer'];
|
||
for (var i in leftBarItems) {
|
||
QC(leftBarItems[i]).remove('lbbuttonsel');
|
||
QC(leftBarItems[i]).remove('lbbuttonsel2');
|
||
}
|
||
|
||
// Define class for Menu(s) as fully or semi active.
|
||
var mainMenuActiveClass = (x < 9 ? 'fullselect' : 'semiselect');
|
||
var leftMenuActiveClass = (x < 9 ? 'lbbuttonsel2' : 'lbbuttonsel');
|
||
|
||
// My Devices
|
||
if (x == 1 || (x >= 10 && x < 20)) QC('MainMenuMyDevices').add(mainMenuActiveClass);
|
||
if (x == 1 || (x >= 10 && x < 20)) QC('LeftMenuMyDevices').add(leftMenuActiveClass);
|
||
|
||
// My Account
|
||
if (x == 2 || (x >= 20 && x < 30)) QC('MainMenuMyAccount').add(mainMenuActiveClass);
|
||
if (x == 2 || (x >= 20 && x < 30)) QC('LeftMenuMyAccount').add(leftMenuActiveClass);
|
||
|
||
// My Events
|
||
if (x == 3) QC('MainMenuMyEvents').add(mainMenuActiveClass);
|
||
if (x == 3) QC('LeftMenuMyEvents').add(leftMenuActiveClass);
|
||
|
||
// My Users
|
||
if (x == 4 || (x >= 30 && x < 40)) QC('MainMenuMyUsers').add(mainMenuActiveClass);
|
||
if (x == 4 || (x >= 30 && x < 40)) QC('LeftMenuMyUsers').add(leftMenuActiveClass);
|
||
|
||
// My Files
|
||
if (x == 5) QC('MainMenuMyFiles').add(mainMenuActiveClass);
|
||
if (x == 5) QC('LeftMenuMyFiles').add(leftMenuActiveClass);
|
||
|
||
// My Server
|
||
if ((x == 6) || (x == 115)) QC('MainMenuMyServer').add(mainMenuActiveClass);
|
||
if ((x == 6) || (x == 115) || (x == 40)) QC('LeftMenuMyServer').add(leftMenuActiveClass);
|
||
|
||
// column_l max-height
|
||
if (webPageStackMenu && (x >= 10)) { QC('column_l').add('room4submenu'); } else { QC('column_l').remove('room4submenu'); }
|
||
|
||
// If we are going to panel 0 in "full screen mode", hide the left bar.
|
||
QV('topbar', x != 0);
|
||
if ((x == 0) && (webPageFullScreen)) {
|
||
QC('body').add("arg_hide");
|
||
}
|
||
|
||
QV('MainSubMenuSpan', x >= 10 && x < 20);
|
||
QV('UserDummyMenuSpan', (x < 10) && (x != 6) && webPageFullScreen);
|
||
QV('MeshSubMenuSpan', x >= 20 && x < 30);
|
||
QV('UserSubMenuSpan', x >= 30 && x < 40);
|
||
QV('ServerSubMenuSpan', x == 6 || x == 115 || x == 40);
|
||
var panels = { 10: 'MainDev', 11: 'MainDevDesktop', 12: 'MainDevTerminal', 13: 'MainDevFiles', 14: 'MainDevAmt', 15: 'MainDevConsole', 16: 'MainDevEvents', 20: 'MeshGeneral', 30: 'UserGeneral', 31: 'UserEvents', 6: 'ServerGeneral', 40: 'ServerStats', 115: 'ServerConsole' };
|
||
for (var i in panels) {
|
||
QC(panels[i]).remove('style3x');
|
||
QC(panels[i]).remove('style3sel');
|
||
QC(panels[i]).add((x == i) ? 'style3sel' : 'style3x');
|
||
}
|
||
|
||
// If going to the remote desktop tab, adjust the tab.
|
||
if (x == 11) { deskAdjust(); }
|
||
|
||
// Panel 115 is weird, it's panel 15 for device console but used as a server console.
|
||
if (x == 115) { QV('p15', true); }
|
||
QV('p15uploadCore', x != 115);
|
||
QV('p15BackButton', x != 115);
|
||
if ((x == 15) || (x == 115)) { setupConsole(); }
|
||
|
||
if (x == 1) masterUpdate(4);
|
||
|
||
// Setup web notifications
|
||
if ((x == 2) && Notification) { QV('accountEnableNotificationsSpan', Notification.permission != 'granted'); }
|
||
|
||
// Fetch the server timeline stats if needed
|
||
if ((x == 40) && (serverTimelineStats == null)) { refreshServerTimelineStats(); }
|
||
|
||
// Update the web page title
|
||
if ((currentNode) && (x >= 10) && (x < 20)) { document.title = decodeURIComponent("{{{extitle}}}") + ' - ' + currentNode.name; } else { document.title = decodeURIComponent("{{{extitle}}}"); }
|
||
}
|
||
|
||
// Generic methods
|
||
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 putstore(name, val) { try { if ((typeof (localStorage) === 'undefined') || (localStorage.getItem(name) == val)) return; 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 addLink(x, f) { return "<a style=cursor:pointer;color:darkblue;text-decoration:none onclick='" + f + "'>♦ " + x + "</a>"; }
|
||
function addLink(x, f) { return "<span style=cursor:pointer;text-decoration:none onclick='" + f + "'>" + x + " <img class=hoverButton src=images/link5.png></span>"; }
|
||
function addLinkConditional(x, f, c) { if (c) return addLink(x, f); return x; }
|
||
function haltEvent(e) { if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return false; }
|
||
function addOption(q, t, i) { var option = document.createElement("option"); option.text = t; option.value = i; Q(q).add(option); }
|
||
function passwordcheck(p) { return (p.length > 7) && (/\d/.test(p)) && (/[a-z]/.test(p)) && (/[A-Z]/.test(p)) && (/\W/.test(p)); }
|
||
function methodcheck(r) { if (r && r != null && r.Body && r.Body.ReturnValueStr != "SUCCESS") { messagebox("Call Error", r.Header.Method + ": " + r.Body.ReturnValueStr.replace("_", " ")); return true; } return false; }
|
||
function TableStart() { return "<table cellpadding=0 cellspacing=0 style=width:100%;border-radius:8px><tr><td width=200px><p><td>"; }
|
||
function TableStart2() { return "<table cellpadding=0 cellspacing=0 style=width:100%;border-radius:8px><tr><td><p><td>"; }
|
||
function TableEntry(n, v) { return "<tr><td><p>" + n + "<td>" + v; }
|
||
function FullTable(x, e) { var r = TableStart(); for (i in x) { if (i && x[i]) r += TableEntry(i, x[i]); } return r + TableEnd(e); }
|
||
function TableEnd(n) { return "<tr><td colspan=2><p>" + (n?n:'') + "</table>"; }
|
||
function AddButton(v, f) { return "<input type=button value='" + v + "' onclick='" + f + "' style=margin:4px>"; }
|
||
function AddButton2(v, f) { return "<input type=button value='" + v + "' onclick='" + f + "'>"; }
|
||
function AddRefreshButton(f) { return "<input type=button name=refreshbtn value=Refresh onclick='refreshButtons(false);" + f + "' style=margin:4px " + (refreshButtonsState==false?"disabled":"") + ">"; }
|
||
function MoreStart() { return "<a style=cursor:pointer;color:blue id=morexxx1 onclick=QV(\"morexxx1\",false);QV(\"morexxx2\",true)>▼ More</a><div id=morexxx2 style=display:none><br><hr>"; };
|
||
function MoreEnd() { return "<a style=cursor:pointer;color:blue onclick=QV(\"morexxx2\",false);QV(\"morexxx1\",true)>▲ Less</a></div>"; };
|
||
function getSelectedOptions(sel) { var opts = [], opt; for (var i = 0, len = sel.options.length; i < len; i++) { opt = sel.options[i]; if (opt.selected) { opts.push(opt.value); } } return opts; }
|
||
function getInstance(x, y) { for (var i in x) { if (x[i]["InstanceID"] == y) return x[i]; } return null; }
|
||
function getItem(x, y, z) { for (var i in x) { if (x[i][y] == z) return x[i]; } return null; }
|
||
function guidToStr(g) { return g.substring(6, 8) + g.substring(4, 6) + g.substring(2, 4) + g.substring(0, 2) + "-" + g.substring(10, 12) + g.substring(8, 10) + "-" + g.substring(14, 16) + g.substring(12, 14) + "-" + g.substring(16, 20) + "-" + g.substring(20); }
|
||
function getUrlVars() { var j, hash, vars = [], hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); for (var i = 0; i < hashes.length; i++) { j = hashes[i].indexOf('='); if (j > 0) { vars[hashes[i].substring(0, j)] = hashes[i].substring(j + 1, hashes[i].length); } } return vars; }
|
||
//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 addHtmlValue(t, v) { return '<div style=height:20px><div style=float:right;width:220px><b>' + v + '</b></div><div>' + t + '</div></div>'; }
|
||
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 parseUriArgs() { var name, r = {}, parsedUri = window.document.location.href.split(/[\?&|\=]/); parsedUri.splice(0, 1); for (x in parsedUri) { switch (x % 2) { case 0: { name = decodeURIComponent(parsedUri[x]); break; } case 1: { r[name] = decodeURIComponent(parsedUri[x]); var x = parseInt(r[name]); if (x == r[name]) { r[name] = x; } break; } default: { break; } } } return r; }
|
||
function focusTextBox(x) { setTimeout(function(){ Q(x).selectionStart = Q(x).selectionEnd = 65535; Q(x).focus(); }, 0); }
|
||
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); } // New version
|
||
function isPrivateIP(a) { return (a.startsWith('10.') || a.startsWith('172.16.') || a.startsWith('192.168.')); }
|
||
function u2fSupported() { return (window.u2f && ((navigator.userAgent.indexOf('Chrome/') > 0) || (navigator.userAgent.indexOf('Firefox/') > 0) || (navigator.userAgent.indexOf('Opera/') > 0) || (navigator.userAgent.indexOf('Safari/') > 0))); }
|
||
function findOne(arr1, arr2) { if ((arr1 == null) || (arr2 == null)) return false; return arr2.some(function (v) { return arr1.indexOf(v) >= 0; }); };
|
||
function copyTextToClip(txt) { function selectElementText(e) { if (document.selection) { var range = document.body.createTextRange(); range.moveToElementText(e); range.select(); } else if (window.getSelection) { var range = document.createRange(); range.selectNode(e); window.getSelection().removeAllRanges(); window.getSelection().addRange(range); } } var e = document.createElement('DIV'); e.textContent = txt; document.body.appendChild(e); selectElementText(e); document.execCommand('copy'); e.remove(); }
|
||
function printDate(d) { return d.toLocaleDateString(args.locale); }
|
||
function printTime(d) { return d.toLocaleTimeString(args.locale); }
|
||
function printDateTime(d) { return d.toLocaleString(args.locale); }
|
||
</script></body></html> |