From e1b93dbd2946d0f6214b6e46cde3a79db5b97a43 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Thu, 13 Aug 2020 12:29:18 -0700 Subject: [PATCH] Improved file upload to device. --- agents/meshcore.js | 60 +++--- public/scripts/amt-wsman-0.2.0-min.js | 2 +- translate/translate.json | 253 +++++++++++++------------- views/default-mobile.handlebars | 164 +++++++++-------- views/default.handlebars | 177 +++++------------- 5 files changed, 301 insertions(+), 355 deletions(-) diff --git a/agents/meshcore.js b/agents/meshcore.js index 0986e520..77cf14a7 100644 --- a/agents/meshcore.js +++ b/agents/meshcore.js @@ -1268,7 +1268,7 @@ function createMeshCore(agent) { */ // If there is a upload or download active on this connection, close the file - if (this.httprequest.uploadFile) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; } + if (this.httprequest.uploadFile) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; delete this.httprequest.uploadFileid; delete this.httprequest.uploadFilePath; } if (this.httprequest.downloadFile) { fs.closeSync(this.httprequest.downloadFile); delete this.httprequest.downloadFile; } // Clean up WebRTC @@ -1291,31 +1291,18 @@ function createMeshCore(agent) { //sendConsoleText('OnTunnelData, ' + data.length + ', ' + typeof data + ', ' + data); // If this is upload data, save it to file - if (this.httprequest.uploadFile) { - if (typeof data == 'object') { - // Save the data to file being uploaded. - if (this.httprequest.uploadFile) { - try { fs.writeSync(this.httprequest.uploadFile, data); } catch (e) { sendConsoleText('FileSave ERROR'); this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out. - this.write(Buffer.from(JSON.stringify({ action: 'uploadack', reqid: this.httprequest.uploadFileid }))); // Ask for more data. - } - } else if (typeof data == 'string') { - // Close the file and confirm. We need to make this added round trip since websocket deflate compression can cause the last message before a websocket close to not be received. - if (this.httprequest.uploadFile) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; } - this.write(Buffer.from(JSON.stringify({ action: 'uploaddone', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file. - this.end(); + if ((this.httprequest.uploadFile) && (typeof data == 'object') && (data[0] != 123)) { + // Save the data to file being uploaded. + if (data[0] == 0) { + // If data starts with zero, skip the first byte. This is used to escape binary file data from JSON. + try { fs.writeSync(this.httprequest.uploadFile, data, 1, data.length - 1); } catch (e) { sendConsoleText('FileUpload Error'); this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out. + } else { + // If data does not start with zero, save as-is. + try { fs.writeSync(this.httprequest.uploadFile, data); } catch (e) { sendConsoleText('FileUpload Error'); this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out. } + this.write(Buffer.from(JSON.stringify({ action: 'uploadack', reqid: this.httprequest.uploadFileid }))); // Ask for more data. return; } - /* - // If this is a download, send more of the file - if (this.httprequest.downloadFile) { - var buf = Buffer.alloc(4096); - var len = fs.readSync(this.httprequest.downloadFile, buf, 0, 4096, null); - this.httprequest.downloadFilePtr += len; - if (len > 0) { this.write(buf.slice(0, len)); } else { fs.closeSync(this.httprequest.downloadFile); this.httprequest.downloadFile = undefined; this.end(); } - return; - } - */ if (this.httprequest.state == 0) { // Check if this is a relay connection @@ -1867,8 +1854,7 @@ function createMeshCore(agent) { this.on('data', onTunnelControlData); //this.write('MeshCore KVM Hello!1'); - } else if (this.httprequest.protocol == 5) - { + } else if (this.httprequest.protocol == 5) { // // Remote Files // @@ -2104,12 +2090,36 @@ function createMeshCore(agent) { if (this.httprequest.uploadFile != null) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; } if (cmd.path == undefined) break; var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path; + this.httprequest.uploadFilePath = filepath; MeshServerLog('Upload: \"' + filepath + '\"', this.httprequest); try { this.httprequest.uploadFile = fs.openSync(filepath, 'wbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'uploaderror', reqid: cmd.reqid }))); break; } this.httprequest.uploadFileid = cmd.reqid; if (this.httprequest.uploadFile) { this.write(Buffer.from(JSON.stringify({ action: 'uploadstart', reqid: this.httprequest.uploadFileid }))); } break; } + case 'uploaddone': { + // Indicates that an upload is done + if (this.httprequest.uploadFile) { + fs.closeSync(this.httprequest.uploadFile); + this.write(Buffer.from(JSON.stringify({ action: 'uploaddone', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file. + delete this.httprequest.uploadFile; + delete this.httprequest.uploadFileid; + delete this.httprequest.uploadFilePath; + } + break; + } + case 'uploadcancel': { + // Indicates that an upload is canceled + if (this.httprequest.uploadFile) { + fs.closeSync(this.httprequest.uploadFile); + fs.unlinkSync(this.httprequest.uploadFilePath); + this.write(Buffer.from(JSON.stringify({ action: 'uploadcancel', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file. + delete this.httprequest.uploadFile; + delete this.httprequest.uploadFileid; + delete this.httprequest.uploadFilePath; + } + break; + } case 'copy': { // Copy a bunch of files from scpath to dspath for (var i in cmd.names) { diff --git a/public/scripts/amt-wsman-0.2.0-min.js b/public/scripts/amt-wsman-0.2.0-min.js index 293387e7..389c6296 100644 --- a/public/scripts/amt-wsman-0.2.0-min.js +++ b/public/scripts/amt-wsman-0.2.0-min.js @@ -1 +1 @@ -var WsmanStackCreateService=function(e,s,r,a,o,t){var p={};function l(e){if(!e)return"";var s=" ";for(var r in e)e.hasOwnProperty(r)&&0===r.indexOf("@")&&(s+=r.substring(1)+'="'+e[r]+'" ');return s}function w(e){if(!e)return"";if("string"==typeof e)return e;if(e.InstanceID)return''+e.InstanceID+"";var s="";for(var r in e)if(e.hasOwnProperty(r)){if(s+='',e[r].ReferenceParameters){s+="",s+=""+e[r].Address+""+e[r].ReferenceParameters.ResourceURI+"";var a=e[r].ReferenceParameters.SelectorSet.Selector;if(Array.isArray(a))for(var o=0;o"+a[o].Value+"";else s+=""+a.Value+"";s+=""}else s+=e[r];s+=""}return s+=""}return p.NextMessageId=1,p.Address="/wsman",p.comm=CreateWsmanComm(e,s,r,a,o,t),p.PerformAjax=function(e,o,s,r,a){null==a&&(a=""),p.comm.PerformAjax('
"+e,function(e,s,r){if(200==s){var a=p.ParseWsman(e);a&&null!=a?o(p,a.Header.ResourceURI,a,200,r):o(p,null,{Header:{HttpError:s}},601,r)}else o(p,null,{Header:{HttpError:s}},s,r)},s,r)},p.CancelAllQueries=function(e){p.comm.CancelAllQueries(e)},p.GetNameFromUrl=function(e){var s=e.lastIndexOf("/");return-1==s?e:e.substring(s+1)},p.ExecSubscribe=function(e,s,r,a,o,t,n,l,d,c){var m="",i="";null!=d&&null!=c&&(m="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken"+d+''+c+"",i=''),l=null!=l&&null!=l?""+l+"":"";var u="http://schemas.xmlsoap.org/ws/2004/08/eventing/Subscribe"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous"+w(n)+m+'
'+r+""+i+"PT0.000000S";p.PerformAjax(u+"
",a,o,t,'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"')},p.ExecUnSubscribe=function(e,s,r,a,o){var t="http://schemas.xmlsoap.org/ws/2004/08/eventing/Unsubscribe"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous"+w(o)+"";p.PerformAjax(t+"",s,r,a,'xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing"')},p.ExecPut=function(e,s,r,a,o,t){var n="http://schemas.xmlsoap.org/ws/2004/09/transfer/Put"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60.000S"+w(t)+""+function(e,s){if(!e||null==s)return"";var r=p.GetNameFromUrl(e),a="';for(var o in s)if(s.hasOwnProperty(o)&&0!==o.indexOf("__")&&0!==o.indexOf("@")&&void 0!==s[o]&&null!==s[o]&&"function"!=typeof s[o])if("object"==typeof s[o]&&s[o].ReferenceParameters){a+=""+s[o].Address+""+s[o].ReferenceParameters.ResourceURI+"";var t=s[o].ReferenceParameters.SelectorSet.Selector;if(Array.isArray(t))for(var n=0;n"+t[n].Value+"";else a+=""+t.Value+"";a+=""}else if(Array.isArray(s[o]))for(n=0;n"+s[o][n].toString()+"";else a+=""+s[o].toString()+"";return a+=""}(e,s);p.PerformAjax(n+"",r,a,o)},p.ExecCreate=function(e,s,r,a,o,t){var n=p.GetNameFromUrl(e),l="http://schemas.xmlsoap.org/ws/2004/09/transfer/Create"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S"+w(t)+"';for(var d in s)l+=""+s[d]+"";p.PerformAjax(l+"",r,a,o)},p.ExecCreateXml=function(e,s,r,a,o){var t=p.GetNameFromUrl(e);p.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/transfer/Create"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60.000S'+s+"",r,a,o)},p.ExecDelete=function(e,s,r,a,o){var t="http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S"+w(s)+"";p.PerformAjax(t,r,a,o)},p.ExecGet=function(e,s,r,a){p.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/transfer/Get"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S",s,r,a)},p.ExecMethod=function(e,s,r,a,o,t,n){var l="";for(var d in r)if(null!=r[d])if(Array.isArray(r[d]))for(var c in r[d])l+=""+r[d][c]+"";else l+=""+r[d]+"";p.ExecMethodXml(e,s,l,a,o,t,n)},p.ExecMethodXml=function(e,s,r,a,o,t,n){p.PerformAjax(e+"/"+s+""+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S"+w(n)+"'+r+"",a,o,t)},p.ExecEnum=function(e,s,r,a){p.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/enumeration/Enumerate"+p.Address+""+e+""+p.NextMessageId+++'http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S',s,r,a)},p.ExecPull=function(e,s,r,a,o){p.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/enumeration/Pull"+p.Address+""+e+""+p.NextMessageId+++'http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S'+s+"99999999",r,a,o)},p.ParseWsman=function(s){try{s.childNodes||(s=function(e){{if(window.DOMParser)return(new DOMParser).parseFromString(e,"text/xml");var s=new ActiveXObject("Microsoft.XMLDOM");return s.async=!1,s.loadXML(e),s}}(s));var e,r={Header:{}},a=s.getElementsByTagName("Header")[0];if(!(a=a||s.getElementsByTagName("a:Header")[0]))return null;for(var o=0;o'+e.InstanceID+"";var s="";for(var r in e)if(e.hasOwnProperty(r)){if(s+='',e[r].ReferenceParameters){s+="",s+=""+e[r].Address+""+e[r].ReferenceParameters.ResourceURI+"";var a=e[r].ReferenceParameters.SelectorSet.Selector;if(Array.isArray(a))for(var o=0;o"+a[o].Value+"";else s+=""+a.Value+"";s+=""}else s+=e[r];s+=""}return s+=""}return p.NextMessageId=1,p.Address="/wsman",p.comm=CreateWsmanComm(e,s,r,a,o,t),p.PerformAjax=function(e,o,s,r,a){null==a&&(a=""),p.comm.PerformAjax('
"+e,function(e,s,r){if(200==s){var a=p.ParseWsman(e);a&&null!=a?o(p,a.Header.ResourceURI,a,200,r):o(p,null,{Header:{HttpError:s}},601,r)}else o(p,null,{Header:{HttpError:s}},s,r)},s,r)},p.CancelAllQueries=function(e){p.comm.CancelAllQueries(e)},p.GetNameFromUrl=function(e){var s=e.lastIndexOf("/");return-1==s?e:e.substring(s+1)},p.ExecSubscribe=function(e,s,r,a,o,t,n,l,c,d){var m="",i="";null!=c&&null!=d&&(m="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken"+c+''+d+"",i=''),l=null!=l&&null!=l?""+l+"":"";var u="http://schemas.xmlsoap.org/ws/2004/08/eventing/Subscribe"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous"+w(n)+m+'
'+r+""+i+"PT0.000000S";p.PerformAjax(u+"
",a,o,t,'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"')},p.ExecUnSubscribe=function(e,s,r,a,o){var t="http://schemas.xmlsoap.org/ws/2004/08/eventing/Unsubscribe"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous"+w(o)+"";p.PerformAjax(t+"",s,r,a,'xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing"')},p.ExecPut=function(e,s,r,a,o,t){var n="http://schemas.xmlsoap.org/ws/2004/09/transfer/Put"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60.000S"+w(t)+""+function(e,s){if(!e||null==s)return"";var r=p.GetNameFromUrl(e),a="';for(var o in s)if(s.hasOwnProperty(o)&&0!==o.indexOf("__")&&0!==o.indexOf("@")&&void 0!==s[o]&&null!==s[o]&&"function"!=typeof s[o])if("object"==typeof s[o]&&s[o].ReferenceParameters){a+=""+s[o].Address+""+s[o].ReferenceParameters.ResourceURI+"";var t=s[o].ReferenceParameters.SelectorSet.Selector;if(Array.isArray(t))for(var n=0;n"+t[n].Value+"";else a+=""+t.Value+"";a+=""}else if(Array.isArray(s[o]))for(n=0;n"+s[o][n].toString()+"";else a+=""+s[o].toString()+"";return a+=""}(e,s);p.PerformAjax(n+"",r,a,o)},p.ExecCreate=function(e,s,r,a,o,t){var n=p.GetNameFromUrl(e),l="http://schemas.xmlsoap.org/ws/2004/09/transfer/Create"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S"+w(t)+"';for(var c in s)l+=""+s[c]+"";p.PerformAjax(l+"",r,a,o)},p.ExecCreateXml=function(e,s,r,a,o){var t=p.GetNameFromUrl(e);p.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/transfer/Create"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60.000S'+s+"",r,a,o)},p.ExecDelete=function(e,s,r,a,o){var t="http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S"+w(s)+"";p.PerformAjax(t,r,a,o)},p.ExecGet=function(e,s,r,a){p.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/transfer/Get"+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S",s,r,a)},p.ExecMethod=function(e,s,r,a,o,t,n){var l="";for(var c in r)if(null!=r[c])if(Array.isArray(r[c]))for(var d in r[c])l+=""+r[c][d]+"";else l+=""+r[c]+"";p.ExecMethodXml(e,s,l,a,o,t,n)},p.ExecMethodXml=function(e,s,r,a,o,t,n){p.PerformAjax(e+"/"+s+""+p.Address+""+e+""+p.NextMessageId+++"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S"+w(n)+"'+r+"",a,o,t)},p.ExecEnum=function(e,s,r,a){p.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/enumeration/Enumerate"+p.Address+""+e+""+p.NextMessageId+++'http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S',s,r,a)},p.ExecPull=function(e,s,r,a,o){p.PerformAjax("http://schemas.xmlsoap.org/ws/2004/09/enumeration/Pull"+p.Address+""+e+""+p.NextMessageId+++'http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousPT60S'+s+"99999999",r,a,o)},p.ParseWsman=function(s){try{s.childNodes||(s=function(e){{if(window.DOMParser)return(new DOMParser).parseFromString(e,"text/xml");var s=new ActiveXObject("Microsoft.XMLDOM");return s.async=!1,s.loadXML(e),s}}(s));var e,r={Header:{}},a=s.getElementsByTagName("Header")[0];if(!(a=a||s.getElementsByTagName("a:Header")[0]))return null;for(var o=0;o9->446", + "default-mobile.handlebars->9->449", "default.handlebars->27->1439" ] }, @@ -733,7 +733,7 @@ "fi": "1 tavu", "xloc": [ "default-mobile.handlebars->9->118", - "default-mobile.handlebars->9->450", + "default-mobile.handlebars->9->453", "default.handlebars->27->1499" ] }, @@ -2369,7 +2369,7 @@ "xloc": [ "default-mobile.handlebars->9->226", "default-mobile.handlebars->9->228", - "default-mobile.handlebars->9->355", + "default-mobile.handlebars->9->358", "default.handlebars->27->553", "default.handlebars->27->555", "default.handlebars->27->930" @@ -2688,7 +2688,7 @@ "zh-chs": "添加用戶", "fi": "Lisää Käyttäjä", "xloc": [ - "default-mobile.handlebars->9->395", + "default-mobile.handlebars->9->398", "default.handlebars->27->648" ] }, @@ -2757,7 +2757,7 @@ "zh-chs": "将用户添加到设备组", "fi": "Lisää käyttäjä laiteryhmään", "xloc": [ - "default-mobile.handlebars->9->424" + "default-mobile.handlebars->9->427" ] }, { @@ -2996,7 +2996,7 @@ "ru": "Режим управления администратора (ACM)", "zh-chs": "管理員控制模式(ACM)", "xloc": [ - "default-mobile.handlebars->9->357", + "default-mobile.handlebars->9->360", "default.handlebars->27->932" ] }, @@ -3014,7 +3014,7 @@ "ru": "Учетные данные администратора", "zh-chs": "管理員憑證", "xloc": [ - "default-mobile.handlebars->9->363", + "default-mobile.handlebars->9->366", "default.handlebars->27->938" ] }, @@ -3200,7 +3200,7 @@ "zh-chs": "代理控制台", "fi": "Agentin konsoli", "xloc": [ - "default-mobile.handlebars->9->430", + "default-mobile.handlebars->9->433", "default.handlebars->27->1422" ] }, @@ -4178,7 +4178,7 @@ "zh-chs": "建築", "fi": "Arkkitehtuuri", "xloc": [ - "default-mobile.handlebars->9->327", + "default-mobile.handlebars->9->330", "default.handlebars->27->892" ] }, @@ -4215,7 +4215,7 @@ "zh-chs": "您確定要刪除組{0}嗎?刪除設備組還將刪除該組中有關設備的所有信息。", "fi": "Haluatko varmasti poistaa ryhmän {0}? Laiteryhmän poistaminen poistaa myös kaikki tämän ryhmän laitteiden tiedot.", "xloc": [ - "default-mobile.handlebars->9->401", + "default-mobile.handlebars->9->404", "default.handlebars->27->1348" ] }, @@ -4555,7 +4555,7 @@ "ru": "BIOS", "zh-chs": "的BIOS", "xloc": [ - "default-mobile.handlebars->9->369", + "default-mobile.handlebars->9->372", "default.handlebars->27->944" ] }, @@ -5054,7 +5054,7 @@ "ru": "CPU", "zh-chs": "CPU", "xloc": [ - "default-mobile.handlebars->9->375", + "default-mobile.handlebars->9->378", "default.handlebars->27->950" ] }, @@ -5234,8 +5234,8 @@ "zh-chs": "容量", "fi": "Kapasiteetti", "xloc": [ - "default-mobile.handlebars->9->383", - "default-mobile.handlebars->9->385", + "default-mobile.handlebars->9->386", + "default-mobile.handlebars->9->388", "default.handlebars->27->958", "default.handlebars->27->960" ] @@ -5255,7 +5255,7 @@ "zh-chs": "容量/速度", "fi": "Kapasiteetti / Nopeus", "xloc": [ - "default-mobile.handlebars->9->378", + "default-mobile.handlebars->9->381", "default.handlebars->27->953" ] }, @@ -5585,8 +5585,8 @@ "ru": "Чаты и уведомления", "zh-chs": "聊天並通知", "xloc": [ - "default-mobile.handlebars->9->422", - "default-mobile.handlebars->9->440", + "default-mobile.handlebars->9->425", + "default-mobile.handlebars->9->443", "default.handlebars->27->1398", "default.handlebars->27->1433" ] @@ -6125,7 +6125,7 @@ "ru": "Режим управления клиентом (CCM)", "zh-chs": "客戶端控制模式(CCM)", "xloc": [ - "default-mobile.handlebars->9->356", + "default-mobile.handlebars->9->359", "default.handlebars->27->931" ] }, @@ -6287,7 +6287,7 @@ "fi": "Varmista", "xloc": [ "default-mobile.handlebars->9->276", - "default-mobile.handlebars->9->402", + "default-mobile.handlebars->9->405", "default.handlebars->27->1349", "default.handlebars->27->1579", "default.handlebars->27->1657", @@ -6662,7 +6662,7 @@ "ru": "Подтвердить удаление пользователя {0}?", "zh-chs": "確認刪除用戶{0}?", "xloc": [ - "default-mobile.handlebars->9->449" + "default-mobile.handlebars->9->452" ] }, { @@ -6832,7 +6832,7 @@ "ru": "Подключено сейчас", "zh-chs": "現在已連接", "xloc": [ - "default-mobile.handlebars->9->331", + "default-mobile.handlebars->9->334", "default.handlebars->27->896" ] }, @@ -6868,7 +6868,7 @@ "zh-chs": "正在連線...", "xloc": [ "default-mobile.handlebars->9->2", - "default-mobile.handlebars->9->324", + "default-mobile.handlebars->9->327", "default-mobile.handlebars->9->37", "default.handlebars->27->233", "default.handlebars->27->236", @@ -7964,8 +7964,8 @@ "ru": "Удалить группу", "zh-chs": "刪除群組", "xloc": [ - "default-mobile.handlebars->9->400", "default-mobile.handlebars->9->403", + "default-mobile.handlebars->9->406", "default.handlebars->27->1320", "default.handlebars->27->1350" ] @@ -8309,9 +8309,9 @@ "default-mobile.handlebars->9->221", "default-mobile.handlebars->9->222", "default-mobile.handlebars->9->280", - "default-mobile.handlebars->9->337", - "default-mobile.handlebars->9->392", - "default-mobile.handlebars->9->405", + "default-mobile.handlebars->9->340", + "default-mobile.handlebars->9->395", + "default-mobile.handlebars->9->408", "default.handlebars->27->1241", "default.handlebars->27->1269", "default.handlebars->27->1352", @@ -8620,7 +8620,7 @@ "ru": "Пользователь группы устройств", "zh-chs": "設備組用戶", "xloc": [ - "default-mobile.handlebars->9->447", + "default-mobile.handlebars->9->450", "default.handlebars->27->1440" ] }, @@ -10106,9 +10106,9 @@ "zh-chs": "編輯設備組", "fi": "Muokkaa laiteryhmää", "xloc": [ - "default-mobile.handlebars->9->406", - "default-mobile.handlebars->9->408", - "default-mobile.handlebars->9->426", + "default-mobile.handlebars->9->409", + "default-mobile.handlebars->9->411", + "default-mobile.handlebars->9->429", "default.handlebars->27->1353", "default.handlebars->27->1383", "default.handlebars->27->1406", @@ -10181,7 +10181,7 @@ "ru": "Редактировать примечания устройства", "zh-chs": "編輯設備說明", "xloc": [ - "default-mobile.handlebars->9->420", + "default-mobile.handlebars->9->423", "default.handlebars->27->1396" ] }, @@ -10275,7 +10275,7 @@ "zh-chs": "編輯筆記", "fi": "Muokkaa muistiinpanoja", "xloc": [ - "default-mobile.handlebars->9->433", + "default-mobile.handlebars->9->436", "default.handlebars->27->1425" ] }, @@ -12055,9 +12055,9 @@ "fi": "Täysi Järjestelmänvalvoja", "xloc": [ "default-mobile.handlebars->9->105", - "default-mobile.handlebars->9->398", - "default-mobile.handlebars->9->407", - "default-mobile.handlebars->9->425", + "default-mobile.handlebars->9->401", + "default-mobile.handlebars->9->410", + "default-mobile.handlebars->9->428", "default.handlebars->27->1250", "default.handlebars->27->1382", "default.handlebars->27->1633" @@ -12176,7 +12176,7 @@ "nl": "GPU", "zh-chs": "显卡", "xloc": [ - "default-mobile.handlebars->9->376", + "default-mobile.handlebars->9->379", "default.handlebars->27->951" ] }, @@ -13195,8 +13195,8 @@ "ru": "IP: {0}", "zh-chs": "IP:{0}", "xloc": [ - "default-mobile.handlebars->9->345", - "default-mobile.handlebars->9->349", + "default-mobile.handlebars->9->348", + "default-mobile.handlebars->9->352", "default.handlebars->27->910", "default.handlebars->27->920", "default.handlebars->27->924" @@ -13216,8 +13216,8 @@ "ru": "IP: {0}, маска: {1}, шлюз: {2}", "zh-chs": "IP:{0},掩碼:{1},網關:{2}", "xloc": [ - "default-mobile.handlebars->9->343", - "default-mobile.handlebars->9->347", + "default-mobile.handlebars->9->346", + "default-mobile.handlebars->9->350", "default.handlebars->27->908", "default.handlebars->27->918", "default.handlebars->27->922" @@ -13237,8 +13237,8 @@ "ru": "Уровень IPv4", "zh-chs": "IPv4層", "xloc": [ - "default-mobile.handlebars->9->342", - "default-mobile.handlebars->9->344", + "default-mobile.handlebars->9->345", + "default-mobile.handlebars->9->347", "default.handlebars->27->907", "default.handlebars->27->909", "default.handlebars->27->917", @@ -13306,8 +13306,8 @@ "nl": "IPv6 Layer", "es": "Capa IPv6", "xloc": [ - "default-mobile.handlebars->9->346", - "default-mobile.handlebars->9->348", + "default-mobile.handlebars->9->349", + "default-mobile.handlebars->9->351", "default.handlebars->27->921", "default.handlebars->27->923" ] @@ -13385,7 +13385,7 @@ "ru": "Идентификатор", "zh-chs": "識別碼", "xloc": [ - "default-mobile.handlebars->9->374", + "default-mobile.handlebars->9->377", "default.handlebars->27->949" ] }, @@ -14171,7 +14171,7 @@ "ru": "Только Intel® AMT, без агента", "zh-chs": "僅限英特爾®AMT,無代理", "xloc": [ - "default-mobile.handlebars->9->389", + "default-mobile.handlebars->9->392", "default.handlebars->27->1240", "default.handlebars->27->1266" ] @@ -14207,7 +14207,7 @@ "ru": "Intel® Active Management Technology (Intel® AMT)", "zh-chs": "英特爾®主動管理技術(英特爾®AMT)", "xloc": [ - "default-mobile.handlebars->9->366", + "default-mobile.handlebars->9->369", "default.handlebars->27->941" ] }, @@ -15069,7 +15069,7 @@ "ru": "Известный", "zh-chs": "已知的", "xloc": [ - "default-mobile.handlebars->9->365", + "default-mobile.handlebars->9->368", "default.handlebars->27->940" ] }, @@ -15417,9 +15417,9 @@ "ru": "Последний адрес агента", "zh-chs": "最後代理商地址", "xloc": [ - "default-mobile.handlebars->9->333", - "default-mobile.handlebars->9->334", - "default-mobile.handlebars->9->335", + "default-mobile.handlebars->9->336", + "default-mobile.handlebars->9->337", + "default-mobile.handlebars->9->338", "default.handlebars->27->69", "default.handlebars->27->71", "default.handlebars->27->73", @@ -15442,8 +15442,8 @@ "ru": "Последнее подключение агента", "zh-chs": "上次代理連接", "xloc": [ - "default-mobile.handlebars->9->330", - "default-mobile.handlebars->9->332", + "default-mobile.handlebars->9->333", + "default-mobile.handlebars->9->335", "default.handlebars->27->68", "default.handlebars->27->895", "default.handlebars->27->897" @@ -15749,7 +15749,7 @@ "ru": "Ограниченный ввод", "zh-chs": "有限輸入", "xloc": [ - "default-mobile.handlebars->9->438", + "default-mobile.handlebars->9->441", "default.handlebars->27->1431", "default.handlebars->27->661", "default.handlebars->27->680" @@ -15769,7 +15769,7 @@ "ru": "Ограничить элементы ввода", "zh-chs": "僅限於輸入", "xloc": [ - "default-mobile.handlebars->9->413", + "default-mobile.handlebars->9->416", "default.handlebars->27->1388" ] }, @@ -16542,8 +16542,8 @@ "ru": "MAC-уровень", "zh-chs": "MAC層", "xloc": [ - "default-mobile.handlebars->9->338", - "default-mobile.handlebars->9->340", + "default-mobile.handlebars->9->341", + "default-mobile.handlebars->9->343", "default.handlebars->27->903", "default.handlebars->27->905", "default.handlebars->27->913", @@ -16582,7 +16582,7 @@ "ru": "MAC: {0}", "zh-chs": "MAC:{0}", "xloc": [ - "default-mobile.handlebars->9->341", + "default-mobile.handlebars->9->344", "default.handlebars->27->906", "default.handlebars->27->916" ] @@ -16601,7 +16601,7 @@ "ru": "MAC: {0}, шлюз: {1}", "zh-chs": "MAC:{0},網關:{1}", "xloc": [ - "default-mobile.handlebars->9->339", + "default-mobile.handlebars->9->342", "default.handlebars->27->904", "default.handlebars->27->914" ] @@ -16925,8 +16925,8 @@ "zh-chs": "管理設備組計算機", "fi": "Hallitse laiteryhmän tietokoneita", "xloc": [ - "default-mobile.handlebars->9->410", - "default-mobile.handlebars->9->428", + "default-mobile.handlebars->9->413", + "default-mobile.handlebars->9->431", "default.handlebars->27->1385", "default.handlebars->27->1420" ] @@ -16946,8 +16946,8 @@ "zh-chs": "管理設備組用戶", "fi": "Hallitse laiteryhmän käyttäjiä", "xloc": [ - "default-mobile.handlebars->9->409", - "default-mobile.handlebars->9->427", + "default-mobile.handlebars->9->412", + "default-mobile.handlebars->9->430", "default.handlebars->27->1384", "default.handlebars->27->1419" ] @@ -17157,7 +17157,7 @@ "ru": "Управляется с помощью программного агента", "zh-chs": "使用軟件代理進行管理", "xloc": [ - "default-mobile.handlebars->9->390", + "default-mobile.handlebars->9->393", "default.handlebars->27->1267" ] }, @@ -17350,7 +17350,7 @@ "ru": "ОЗУ", "zh-chs": "記憶", "xloc": [ - "default-mobile.handlebars->9->381", + "default-mobile.handlebars->9->384", "default.handlebars->27->1901", "default.handlebars->27->956", "default.handlebars->container->column_l->p40->3->1->p40type->3" @@ -17371,8 +17371,8 @@ "zh-chs": "網格代理", "xloc": [ "default-mobile.handlebars->9->250", - "default-mobile.handlebars->9->329", - "default-mobile.handlebars->9->336", + "default-mobile.handlebars->9->332", + "default-mobile.handlebars->9->339", "default.handlebars->27->378", "default.handlebars->27->382", "default.handlebars->27->391", @@ -17399,7 +17399,7 @@ "ru": "Консоль Mesh Agent", "zh-chs": "網格代理控制台", "xloc": [ - "default-mobile.handlebars->9->417", + "default-mobile.handlebars->9->420", "default.handlebars->27->1393" ] }, @@ -17973,7 +17973,7 @@ "nl": "Model", "zh-chs": "模型", "xloc": [ - "default-mobile.handlebars->9->382", + "default-mobile.handlebars->9->385", "default.handlebars->27->957" ] }, @@ -18043,7 +18043,7 @@ "ru": "Материнская плата", "zh-chs": "母板", "xloc": [ - "default-mobile.handlebars->9->377", + "default-mobile.handlebars->9->380", "default.handlebars->27->952" ] }, @@ -18369,10 +18369,10 @@ "fi": "Nimi", "xloc": [ "default-mobile.handlebars->9->215", - "default-mobile.handlebars->9->325", - "default-mobile.handlebars->9->371", - "default-mobile.handlebars->9->391", - "default-mobile.handlebars->9->404", + "default-mobile.handlebars->9->328", + "default-mobile.handlebars->9->374", + "default-mobile.handlebars->9->394", + "default-mobile.handlebars->9->407", "default-mobile.handlebars->9->97", "default-mobile.handlebars->container->page_content->column_l->p10->p10desktop->deskarea3->deskarea3x->DeskTools->5->1->1", "default.handlebars->27->1237", @@ -18527,7 +18527,7 @@ "ru": "сетей", "zh-chs": "聯網", "xloc": [ - "default-mobile.handlebars->9->350", + "default-mobile.handlebars->9->353", "default.handlebars->27->911", "default.handlebars->27->925" ] @@ -18807,7 +18807,7 @@ "ru": "Нет доступа к файлам", "zh-chs": "沒有文件訪問", "xloc": [ - "default-mobile.handlebars->9->415", + "default-mobile.handlebars->9->418", "default.handlebars->27->1391" ] }, @@ -18826,7 +18826,7 @@ "zh-chs": "沒有文件", "fi": "Ei tiedostoja", "xloc": [ - "default-mobile.handlebars->9->436", + "default-mobile.handlebars->9->439", "default.handlebars->27->1429", "default.handlebars->27->659", "default.handlebars->27->678" @@ -18863,8 +18863,8 @@ "ru": "Нет Intel® AMT", "zh-chs": "沒有英特爾®AMT", "xloc": [ - "default-mobile.handlebars->9->416", - "default-mobile.handlebars->9->437", + "default-mobile.handlebars->9->419", + "default-mobile.handlebars->9->440", "default.handlebars->27->1392", "default.handlebars->27->1430" ] @@ -18989,8 +18989,8 @@ "zh-chs": "沒有權利", "xloc": [ "default-mobile.handlebars->9->106", - "default-mobile.handlebars->9->399", - "default-mobile.handlebars->9->442", + "default-mobile.handlebars->9->402", + "default-mobile.handlebars->9->445", "default.handlebars->27->1251", "default.handlebars->27->1435", "default.handlebars->27->671", @@ -19030,7 +19030,7 @@ "ru": "Нет терминала", "zh-chs": "沒有終端", "xloc": [ - "default-mobile.handlebars->9->435", + "default-mobile.handlebars->9->438", "default.handlebars->27->1428", "default.handlebars->27->658", "default.handlebars->27->677" @@ -19050,7 +19050,7 @@ "ru": "Нет доступа к терминалу", "zh-chs": "沒有終端訪問", "xloc": [ - "default-mobile.handlebars->9->414", + "default-mobile.handlebars->9->417", "default.handlebars->27->1390" ] }, @@ -19291,7 +19291,7 @@ "ru": "Информации об этом устройстве нет", "zh-chs": "沒有此設備的信息。", "xloc": [ - "default-mobile.handlebars->9->387", + "default-mobile.handlebars->9->390", "default.handlebars->27->962" ] }, @@ -19495,7 +19495,7 @@ "default-mobile.handlebars->9->220", "default-mobile.handlebars->9->245", "default-mobile.handlebars->9->297", - "default-mobile.handlebars->9->393", + "default-mobile.handlebars->9->396", "default.handlebars->27->1263", "default.handlebars->27->1270", "default.handlebars->27->1274", @@ -19607,7 +19607,7 @@ "zh-chs": "未激活(輸入)", "xloc": [ "default-mobile.handlebars->9->225", - "default-mobile.handlebars->9->354", + "default-mobile.handlebars->9->357", "default.handlebars->27->552", "default.handlebars->27->929" ] @@ -19627,7 +19627,7 @@ "zh-chs": "未激活(預)", "xloc": [ "default-mobile.handlebars->9->224", - "default-mobile.handlebars->9->353", + "default-mobile.handlebars->9->356", "default.handlebars->27->551", "default.handlebars->27->928" ] @@ -19664,7 +19664,7 @@ "ru": "Неизвестный", "zh-chs": "未知", "xloc": [ - "default-mobile.handlebars->9->364", + "default-mobile.handlebars->9->367", "default.handlebars->27->939" ] }, @@ -20198,7 +20198,7 @@ "ru": "Операционная система", "zh-chs": "操作系統", "xloc": [ - "default-mobile.handlebars->9->328", + "default-mobile.handlebars->9->331", "default.handlebars->27->332", "default.handlebars->27->365", "default.handlebars->27->575", @@ -20381,7 +20381,7 @@ "ru": "Part Number", "zh-chs": "零件號", "xloc": [ - "default-mobile.handlebars->9->380", + "default-mobile.handlebars->9->383", "default.handlebars->27->955" ] }, @@ -20445,7 +20445,7 @@ "zh-chs": "部分權利", "xloc": [ "default-mobile.handlebars->9->104", - "default-mobile.handlebars->9->397", + "default-mobile.handlebars->9->400", "default.handlebars->27->1249" ] }, @@ -20918,7 +20918,7 @@ "zh-chs": "權限", "fi": "Käyttöoikeudet", "xloc": [ - "default-mobile.handlebars->9->445", + "default-mobile.handlebars->9->448", "default.handlebars->27->1438", "default.handlebars->27->1545" ] @@ -21618,7 +21618,7 @@ "ru": "Предоставление государства", "zh-chs": "供應國", "xloc": [ - "default-mobile.handlebars->9->358", + "default-mobile.handlebars->9->361", "default.handlebars->27->933" ] }, @@ -22302,8 +22302,8 @@ "zh-chs": "遙控", "fi": "Etähallinta", "xloc": [ - "default-mobile.handlebars->9->411", - "default-mobile.handlebars->9->429", + "default-mobile.handlebars->9->414", + "default-mobile.handlebars->9->432", "default.handlebars->27->1386", "default.handlebars->27->1421" ] @@ -22378,7 +22378,7 @@ "ru": "Удаленный пользователь Mesh", "zh-chs": "遠程網狀用戶", "xloc": [ - "default-mobile.handlebars->9->448" + "default-mobile.handlebars->9->451" ] }, { @@ -22410,8 +22410,8 @@ "ru": "Только просмотр экрана без ввода", "zh-chs": "僅遠程查看", "xloc": [ - "default-mobile.handlebars->9->412", - "default-mobile.handlebars->9->434", + "default-mobile.handlebars->9->415", + "default-mobile.handlebars->9->437", "default.handlebars->27->1387", "default.handlebars->27->1426" ] @@ -23796,7 +23796,7 @@ "ru": "Защищено с помощью TLS", "zh-chs": "使用TLS保護", "xloc": [ - "default-mobile.handlebars->9->361", + "default-mobile.handlebars->9->364", "default.handlebars->27->936" ] }, @@ -23816,7 +23816,7 @@ "fi": "Turvallisuus", "xloc": [ "default-mobile.handlebars->9->270", - "default-mobile.handlebars->9->360", + "default-mobile.handlebars->9->363", "default.handlebars->27->1766", "default.handlebars->27->286", "default.handlebars->27->732", @@ -24042,7 +24042,7 @@ "ru": "Только собственные события", "zh-chs": "僅自我事件", "xloc": [ - "default-mobile.handlebars->9->439", + "default-mobile.handlebars->9->442", "default.handlebars->27->1432" ] }, @@ -24328,7 +24328,7 @@ "ru": "Серийный номер", "zh-chs": "序列號", "xloc": [ - "default-mobile.handlebars->9->372", + "default-mobile.handlebars->9->375", "default.handlebars->27->947" ] }, @@ -24397,8 +24397,8 @@ "ru": "Серверные файлы", "zh-chs": "服務器文件", "xloc": [ - "default-mobile.handlebars->9->418", - "default-mobile.handlebars->9->431", + "default-mobile.handlebars->9->421", + "default-mobile.handlebars->9->434", "default.handlebars->27->1394", "default.handlebars->27->1423", "default.handlebars->27->1631", @@ -25048,7 +25048,7 @@ "ru": "Показывать только собственные события", "zh-chs": "只顯示自己的事件", "xloc": [ - "default-mobile.handlebars->9->421", + "default-mobile.handlebars->9->424", "default.handlebars->27->1397" ] }, @@ -26274,7 +26274,7 @@ "nl": "Opslag", "zh-chs": "存储", "xloc": [ - "default-mobile.handlebars->9->386", + "default-mobile.handlebars->9->389", "default.handlebars->27->961" ] }, @@ -26637,7 +26637,7 @@ "ru": "TLS не настроен", "zh-chs": "未設置TLS", "xloc": [ - "default-mobile.handlebars->9->362", + "default-mobile.handlebars->9->365", "default.handlebars->27->937" ] }, @@ -27938,7 +27938,7 @@ "ru": "Удаленный ввод", "zh-chs": "類型", "xloc": [ - "default-mobile.handlebars->9->394", + "default-mobile.handlebars->9->397", "default-mobile.handlebars->9->98", "default.handlebars->27->1238", "default.handlebars->27->1271", @@ -28201,7 +28201,7 @@ "ru": "Удаление", "zh-chs": "卸載", "xloc": [ - "default-mobile.handlebars->9->441", + "default-mobile.handlebars->9->444", "default.handlebars->27->1434", "default.handlebars->27->670", "default.handlebars->27->689" @@ -28221,7 +28221,7 @@ "ru": "Удаление агента", "zh-chs": "卸載代理", "xloc": [ - "default-mobile.handlebars->9->423", + "default-mobile.handlebars->9->426", "default.handlebars->27->1399", "default.handlebars->27->444", "default.handlebars->27->720" @@ -28261,8 +28261,8 @@ "default-mobile.handlebars->9->204", "default-mobile.handlebars->9->34", "default-mobile.handlebars->9->35", - "default-mobile.handlebars->9->352", - "default-mobile.handlebars->9->359", + "default-mobile.handlebars->9->355", + "default-mobile.handlebars->9->362", "default-mobile.handlebars->9->6", "default.handlebars->27->107", "default.handlebars->27->108", @@ -28296,7 +28296,7 @@ "ru": "Неизвестно #{0}", "zh-chs": "未知#{0}", "xloc": [ - "default-mobile.handlebars->9->388", + "default-mobile.handlebars->9->391", "default.handlebars->27->1265" ] }, @@ -28534,6 +28534,7 @@ "default-mobile.handlebars->9->126", "default-mobile.handlebars->9->305", "default-mobile.handlebars->9->323", + "default-mobile.handlebars->9->326", "default.handlebars->27->1507", "default.handlebars->27->1515", "default.handlebars->27->860", @@ -28624,6 +28625,7 @@ "ru": "Загрузка перезапишет 1 файл. Продолжить?", "zh-chs": "上傳將覆蓋1個文件。繼續?", "xloc": [ + "default-mobile.handlebars->9->324", "default.handlebars->27->1516", "default.handlebars->27->884" ] @@ -28642,6 +28644,7 @@ "ru": "Загрузка перезапишет {0} файлов. Продолжить?", "zh-chs": "上傳將覆蓋{0}個文件。繼續?", "xloc": [ + "default-mobile.handlebars->9->325", "default.handlebars->27->1517", "default.handlebars->27->885" ] @@ -28838,7 +28841,7 @@ "ru": "Полномочия пользователя", "zh-chs": "用戶授權", "xloc": [ - "default-mobile.handlebars->9->396", + "default-mobile.handlebars->9->399", "default.handlebars->27->1317", "default.handlebars->27->650" ] @@ -28925,7 +28928,7 @@ "nl": "Gebruikers ID", "es": "ID de usuario", "xloc": [ - "default-mobile.handlebars->9->444" + "default-mobile.handlebars->9->447" ] }, { @@ -28987,7 +28990,7 @@ "ru": "Имя пользователя", "zh-chs": "用戶名", "xloc": [ - "default-mobile.handlebars->9->443", + "default-mobile.handlebars->9->446", "default.handlebars->27->1436" ] }, @@ -29327,8 +29330,8 @@ "ru": "Вендор", "zh-chs": "供應商", "xloc": [ - "default-mobile.handlebars->9->367", "default-mobile.handlebars->9->370", + "default-mobile.handlebars->9->373", "default.handlebars->27->942", "default.handlebars->27->945" ] @@ -29442,10 +29445,10 @@ "zh-chs": "版", "fi": "Versio", "xloc": [ - "default-mobile.handlebars->9->326", - "default-mobile.handlebars->9->351", - "default-mobile.handlebars->9->368", - "default-mobile.handlebars->9->373", + "default-mobile.handlebars->9->329", + "default-mobile.handlebars->9->354", + "default-mobile.handlebars->9->371", + "default-mobile.handlebars->9->376", "default.handlebars->27->891", "default.handlebars->27->926", "default.handlebars->27->943", @@ -29688,8 +29691,8 @@ "ru": "Разбудить устройства", "zh-chs": "喚醒設備", "xloc": [ - "default-mobile.handlebars->9->419", - "default-mobile.handlebars->9->432", + "default-mobile.handlebars->9->422", + "default-mobile.handlebars->9->435", "default.handlebars->27->1395", "default.handlebars->27->1424" ] @@ -32016,7 +32019,7 @@ "ru": "{0} Mб", "zh-chs": "{0} Mb", "xloc": [ - "default-mobile.handlebars->9->384", + "default-mobile.handlebars->9->387", "default.handlebars->27->1491", "default.handlebars->27->959" ] @@ -32035,7 +32038,7 @@ "ru": "{0} Мб, {1} Мгц", "zh-chs": "{0} Mb,{1} Mhz", "xloc": [ - "default-mobile.handlebars->9->379", + "default-mobile.handlebars->9->382", "default.handlebars->27->954" ] }, diff --git a/views/default-mobile.handlebars b/views/default-mobile.handlebars index b62230a6..4a67ac2a 100644 --- a/views/default-mobile.handlebars +++ b/views/default-mobile.handlebars @@ -3304,27 +3304,31 @@ var p13filetreelocation = []; function p13gotFiles(data) { - setSessionActivity(); - //console.log('p13gotFiles', data); - if ((data.length > 0) && (data.charCodeAt(0) != 123)) { p13gotDownloadBinaryData(data); return; } + if ((data.length > 0) && (data.charCodeAt(0) != 123)) { p13gotDownloadBinaryData(data); return; } // This is ok because 4 first bytes is a control value. //console.log('p13gotFiles', data); data = JSON.parse(decode_utf8(data)); if (data.action == 'download') { p13gotDownloadCommand(data); return; } - 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 + + // Process file upload commands + if ((data.action != null) && (data.action.startsWith('upload'))) { p13gotUploadData(data); return; } + + if (data.path != null) { + data.path = data.path.replace(/\//g, '\\'); + if ((p13filetree != null) && (data.path == p13filetree.path)) { + // This is an update to the same folder + var checkedNames = p13getCheckedNames(); p13filetree = data; - p13updateFiles(); + 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(); + } } } } @@ -3614,35 +3618,35 @@ var uploadFile; function p13doUploadFiles(files) { if (xxdialogMode) return; + + // Check if we are going to overwrite any files + var winAgent = ((currentNode.agent.id > 0) && (currentNode.agent.id < 5)); + var targetFiles = [], overWriteCount = 0; + for (var i in p13filetree.dir) { if (winAgent) { targetFiles.push(p13filetree.dir[i].n.toLowerCase()); } else { targetFiles.push(p13filetree.dir[i].n); } } + for (var i = 0; i < files.length; i++) { + if (winAgent) { + if (targetFiles.indexOf(files[i].name.toLowerCase()) >= 0) { overWriteCount++; } + } else { + if (targetFiles.indexOf(files[i].name) >= 0) { overWriteCount++; } + } + } + + if (overWriteCount == 0) { + // If no overwrite, go ahead with upload + p13uploadFileContinue(1, files); + } else { + // Otherwise, prompt for confirmation + setDialogMode(2, "Upload File", 3, p13uploadFileContinue, format((overWriteCount == 1) ? "Upload will overwrite 1 file. Continue?" : "Upload will overwrite {0} files. Continue?", overWriteCount), files); + } + } + + function p13uploadFileContinue(b, files) { uploadFile = {}; uploadFile.xpath = p13filetreelocation.join('/'); uploadFile.xfiles = files; uploadFile.xfilePtr = -1; setDialogMode(2, "Upload File", 10, p13uploadFileCancel, '
' + "Connecting..." + '

'); - p13uploadReconnect(); - } - - function onFileUploadStateChange(xdownloadFile, state) { - switch (state) { - case 0: - p13folderup(9999); - break; - case 3: - p13uploadNextFile(); - break; - default: - console.log('Unknown onFileUploadStateChange state', state); - break; - } - } - - // Connect again - function p13uploadReconnect() { - uploadFile.ws = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotUploadData), serverPublicNamePort, authCookie, authRelayCookie, domainUrl); - uploadFile.ws.attemptWebRTC = false; - uploadFile.ws.ctrlMsgAllowed = false; - uploadFile.ws.onStateChanged = onFileUploadStateChange; - uploadFile.ws.Start(filesNode._id); + p13uploadNextFile(); } // Push the next file @@ -3654,61 +3658,69 @@ 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({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xdata.byteLength }); - }; - uploadFile.xreader.readAsArrayBuffer(file); + if (file.xdata == null) { + // Load the data + uploadFile.xreader = new FileReader(); + uploadFile.xreader.onload = function () { + uploadFile.xdata = uploadFile.xreader.result; + files.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xdata.byteLength })); + }; + uploadFile.xreader.readAsArrayBuffer(file); + } else { + // Data already loaded + uploadFile.xdata = file.xdata; + files.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xdata.byteLength })); + } } else { - p13uploadFileCancel(); + p13uploadFileTransferDone(); } } // 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 + if (uploadFile != null) { files.sendText(JSON.stringify({ action: 'uploadcancel', reqid: uploadFile.xfilePtr })); uploadFile = null; } + p13uploadFileTransferDone(); + } + + // Used to cancel the entire transfer. + function p13uploadFileTransferDone() { + uploadFile = null; // No more files to upload, clean up. + setDialogMode(0); // Close the dialog box + p13folderup(9999); // Refresh the current folder } // Receive upload ack from the mesh agent, use this to keep sending more data - function p13gotUploadData(data) { - var cmd = JSON.parse(data); + function p13gotUploadData(cmd) { 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(); + switch (cmd.action) { + case 'uploadstart': { p13uploadNextPart(false); for (var i = 0; i < 8; i++) { p13uploadNextPart(true); } break; } // Send 8 more blocks of 16k to fill the websocket. + case 'uploadack': { p13uploadNextPart(false); break; } + case 'uploaddone': { if (uploadFile.xfiles.length > uploadFile.xfilePtr + 1) { p13uploadNextFile(); } else { p13uploadFileTransferDone(); } break; } + case 'uploaderror': { p13uploadFileCancel(); break; } } } // Push the next part of the file into the websocket. If dataPriming is true, push more data only if it's not the last block of the file. function p13uploadNextPart(dataPriming) { - var data = uploadFile.xdata; - 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(); } + var data = uploadFile.xdata, start = uploadFile.xptr; + if (start >= data.byteLength) { + files.sendText(JSON.stringify({ action: 'uploaddone', reqid: uploadFile.xfilePtr })); } else { - var datapart = data.slice(start, end); - uploadFile.ws.send(datapart); + var end = uploadFile.xptr + 16384; + if (end > data.byteLength) { if (dataPriming == true) { return; } end = data.byteLength; } + var dataslice = new Uint8Array(data.slice(start, end)) + if ((dataslice[0] == 123) || (dataslice[0] == 0)) { + var datapart = new Uint8Array(end - start + 1); + datapart.set(dataslice, 1); // Add a zero char at the start of the send, this will indicate that it's not a JSON command. + files.send(datapart); + } else { + files.send(dataslice); // The data does not start with 0 or 123 "{" so it can't be confused for JSON. + } uploadFile.xptr = end; Q('d2progressBar').value = end; } } + // // DEVICE DETAILS // diff --git a/views/default.handlebars b/views/default.handlebars index 2f7007bd..9f6db7e6 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -7713,21 +7713,27 @@ //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 + + // Process file upload commands + if ((data.action != null) && (data.action.startsWith('upload'))) { p13gotUploadData(data); return; } + + if (data.path != null) { + data.path = data.path.replace(/\//g, '\\'); + if ((p13filetree != null) && (data.path == p13filetree.path)) { + // This is an update to the same folder + var checkedNames = p13getCheckedNames(); p13filetree = data; - p13updateFiles(); + 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(); + } } } } @@ -8020,68 +8026,6 @@ p13uploadFileContinue(1, [{ name: tag, size: data.byteLength, type: 'text/plain', xdata: data }]); } - /* - var downloadFile; // Global state for file download - - // Called by the html page to start a download, arguments are: path, file name and file size. - function p13downloadfile(x, y, z) { - if (xxdialogMode) return; - downloadFile = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotDownloadData), serverPublicNamePort, authCookie, authRelayCookie, domainUrl); // Create our websocket file transport - downloadFile.ctrlMsgAllowed = false; - downloadFile.onStateChanged = onFileDownloadStateChange; - downloadFile.xpath = decodeURIComponent(x); - downloadFile.xfile = decodeURIComponent(y); - downloadFile.xsize = z; - downloadFile.xtsize = 0; - downloadFile.xstate = 0; - downloadFile.Start(filesNode._id); - setDialogMode(2, "Download File", 10, p13downloadFileCancel, '
' + downloadFile.xfile + '

'); - } - - // 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 // @@ -8117,28 +8061,7 @@ uploadFile.xfiles = files; uploadFile.xfilePtr = -1; setDialogMode(2, "Upload File", 10, p13uploadFileCancel, '
' + "Connecting..." + '

'); - p13uploadReconnect(); - } - - function onFileUploadStateChange(xdownloadFile, state) { - switch (state) { - case 0: - setTimeout(function () { p13folderup(9999); }, 200); // Delay the file refresh - break; - case 3: - p13uploadNextFile(); - break; - default: - break; - } - } - - // Connect again - function p13uploadReconnect() { - uploadFile.ws = CreateAgentRedirect(meshserver, CreateRemoteFiles(p13gotUploadData), serverPublicNamePort, authCookie, authRelayCookie, domainUrl); - uploadFile.ws.attemptWebRTC = false; - uploadFile.ws.onStateChanged = onFileUploadStateChange; - uploadFile.ws.Start(filesNode._id); + p13uploadNextFile(); } // Push the next file @@ -8150,66 +8073,64 @@ QH('p13dfileName', file.name); Q('d2progressBar').max = file.size; Q('d2progressBar').value = 0; - if (file.xdata == null) { // Load the data uploadFile.xreader = new FileReader(); uploadFile.xreader.onload = function () { uploadFile.xdata = uploadFile.xreader.result; - uploadFile.ws.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xdata.byteLength })); + files.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xdata.byteLength })); }; uploadFile.xreader.readAsArrayBuffer(file); } else { // Data already loaded uploadFile.xdata = file.xdata; - uploadFile.ws.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xdata.byteLength })); + files.sendText(JSON.stringify({ action: 'upload', reqid: uploadFile.xfilePtr, path: uploadFile.xpath, name: file.name, size: uploadFile.xdata.byteLength })); } } else { - p13uploadFileCancel(); + p13uploadFileTransferDone(); } } // 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 + if (uploadFile != null) { files.sendText(JSON.stringify({ action: 'uploadcancel', reqid: uploadFile.xfilePtr })); uploadFile = null; } + p13uploadFileTransferDone(); + } + + // Used to cancel the entire transfer. + function p13uploadFileTransferDone() { + uploadFile = null; // No more files to upload, clean up. + setDialogMode(0); // Close the dialog box + p13folderup(9999); // Refresh the current folder } // Receive upload ack from the mesh agent, use this to keep sending more data - function p13gotUploadData(data) { - var cmd = JSON.parse(data); + function p13gotUploadData(cmd) { 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 == 'uploaddone') { - if (uploadFile.ws != null) { uploadFile.ws.Stop(); uploadFile.ws = null; } - if (uploadFile.xfiles.length > uploadFile.xfilePtr + 1) { p13uploadReconnect(); } else { p13uploadFileCancel(); } - } else if (cmd.action == 'uploaderror') { - p13uploadFileCancel(); + switch (cmd.action) { + case 'uploadstart': { p13uploadNextPart(false); for (var i = 0; i < 8; i++) { p13uploadNextPart(true); } break; } // Send 8 more blocks of 16k to fill the websocket. + case 'uploadack': { p13uploadNextPart(false); break; } + case 'uploaddone': { if (uploadFile.xfiles.length > uploadFile.xfilePtr + 1) { p13uploadNextFile(); } else { p13uploadFileTransferDone(); } break; } + case 'uploaderror': { p13uploadFileCancel(); break; } } } // Push the next part of the file into the websocket. If dataPriming is true, push more data only if it's not the last block of the file. function p13uploadNextPart(dataPriming) { - var data = uploadFile.xdata; - var start = uploadFile.xptr; + var data = uploadFile.xdata, start = uploadFile.xptr; if (start >= data.byteLength) { - uploadFile.ws.sendCtrlMsg('{"ctrlChannel":"102938","type":"close"}'); + files.sendText(JSON.stringify({ action: 'uploaddone', reqid: uploadFile.xfilePtr })); } else { var end = uploadFile.xptr + 16384; if (end > data.byteLength) { if (dataPriming == true) { return; } end = data.byteLength; } - var datapart = data.slice(start, end); - uploadFile.ws.send(datapart); + var dataslice = new Uint8Array(data.slice(start, end)) + if ((dataslice[0] == 123) || (dataslice[0] == 0)) { + var datapart = new Uint8Array(end - start + 1); + datapart.set(dataslice, 1); // Add a zero char at the start of the send, this will indicate that it's not a JSON command. + files.send(datapart); + } else { + files.send(dataslice); // The data does not start with 0 or 123 "{" so it can't be confused for JSON. + } uploadFile.xptr = end; Q('d2progressBar').value = end; }