Completed SSH file transfer.

Ylian Saint-Hilaire committed May 24, 2021 at 18:13 UTC 6f67c94d01e22ef40407947eb5badb2f52ae7120
3 files changed +85 -8
apprelays.js
+75 -1
@@ -823,7 +823,6 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
823 obj.uploadHandle = handle;
824 try { obj.ws.send(Buffer.from(JSON.stringify({ action: 'uploadstart', reqid: obj.uploadReqid }))) } catch (ex) { }
825 }
826 -
826 });
827 break;
828 }
@@ -854,6 +853,52 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
853 }
854 break;
855 }
856 + case 'download': {
857 + if (obj.sftp == null) return;
858 + switch (msg.sub) {
859 + case 'start': {
860 + var requestedPath = msg.path;
861 + if (requestedPath.startsWith('/') == false) { requestedPath = '/' + requestedPath; }
862 + obj.downloadFullpath = requestedPath;
863 + obj.downloadId = msg.id;
864 + obj.downloadPosition = 0;
865 + obj.downloadBuffer = Buffer.alloc(16384);
866 + obj.sftp.open(obj.downloadFullpath, 'r', function (err, handle) {
867 + if (err != null) {
868 + try { obj.ws.send(Buffer.from(JSON.stringify({ action: 'download', sub: 'cancel', id: obj.downloadId }))) } catch (ex) { }
869 + } else {
870 + obj.downloadHandle = handle;
871 + // MeshServerLogEx((cmd.ask == 'coredump') ? 104 : 49, [cmd.path], 'Download: \"' + cmd.path + '\"', this.httprequest);
872 + try { obj.ws.send(JSON.stringify({ action: 'download', sub: 'start', id: obj.downloadId })) } catch (ex) { }
873 + }
874 + });
875 + break;
876 + }
877 + case 'startack': {
878 + if ((obj.downloadHandle == null) || (obj.downloadId != msg.id)) break;
879 + obj.downloadPendingBlockCount = (typeof msg.ack == 'number') ? msg.ack : 8;
880 + uploadNextBlock();
881 + break;
882 + }
883 + case 'ack': {
884 + if ((obj.downloadHandle == null) || (obj.downloadId != msg.id)) break;
885 + if (obj.downloadPendingBlockCount == 0) { obj.downloadPendingBlockCount = 1; uploadNextBlock(); }
886 + break;
887 + }
888 + case 'stop': {
889 + if ((obj.downloadHandle == null) || (obj.downloadId != msg.id)) break;
890 + if (obj.downloadHandle != null) { obj.sftp.close(obj.downloadHandle, function () { }); }
891 + delete obj.downloadId;
892 + delete obj.downloadBuffer;
893 + delete obj.downloadHandle;
894 + delete obj.downloadFullpath;
895 + delete obj.downloadPosition;
896 + delete obj.downloadPendingBlockCount;
897 + break;
898 + }
899 + }
900 + break;
901 + }
902 case 'sshauth': {
903 if (obj.sshClient != null) return;
904
@@ -876,6 +921,35 @@ module.exports.CreateSshFilesRelay = function (parent, db, ws, req, domain, user
921 } catch (ex) { console.log(ex); obj.close(); }
922 });
923
924 + function uploadNextBlock() {
925 + if (obj.downloadBuffer == null) return;
926 + obj.sftp.read(obj.downloadHandle, obj.downloadBuffer, 4, obj.downloadBuffer.length - 4, obj.downloadPosition, function (err, len, buf) {
927 + obj.downloadPendingBlockCount--;
928 + if (obj.downloadBuffer == null) return;
929 + if (err != null) {
930 + try { obj.ws.send(Buffer.from(JSON.stringify({ action: 'download', sub: 'cancel', id: obj.downloadId }))) } catch (ex) { }
931 + } else {
932 + obj.downloadPosition += len;
933 + if (len < (obj.downloadBuffer.length - 4)) {
934 + obj.downloadBuffer.writeInt32BE(0x01000001, 0)
935 + if (len > 0) { try { obj.ws.send(obj.downloadBuffer.slice(0, len + 4)); } catch (ex) { console.log(ex); } }
936 + } else {
937 + obj.downloadBuffer.writeInt32BE(0x01000000, 0);
938 + try { obj.ws.send(obj.downloadBuffer.slice(0, len + 4)); } catch (ex) { console.log(ex); }
939 + if (obj.downloadPendingBlockCount > 0) { uploadNextBlock(); }
940 + return;
941 + }
942 + }
943 + if (obj.downloadHandle != null) { obj.sftp.close(obj.downloadHandle, function () { }); }
944 + delete obj.downloadId;
945 + delete obj.downloadBuffer;
946 + delete obj.downloadHandle;
947 + delete obj.downloadFullpath;
948 + delete obj.downloadPosition;
949 + delete obj.downloadPendingBlockCount;
950 + });
951 + }
952 +
953 // If error, do nothing
954 ws.on('error', function (err) { parent.parent.debug('relay', 'SSH: Browser websocket error: ' + err); obj.close(); });
955
public/scripts/agent-redir-ws-0.1.1.js
+1 -1
@@ -214,7 +214,7 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort, au
214 } else { // Process large data block
215 var bb = new Blob([new Uint8Array(e.data)]), f = new FileReader();
216 f.onload = function (e) { obj.m.ProcessData(e.target.result); };
217 - f.readAsText(bb);
217 + f.readAsBinaryString(bb);
218 }
219 }
220 }
views/default.handlebars
+9 -6
@@ -9138,12 +9138,15 @@
9138 } else {
9139 var link = shortname;
9140 if (f.s > 0) {
9141 - // Local link
9142 - //link = '<a href=# style=cursor:pointer onclick="return p13downloadfile(\'' + encodeURIComponentEx(newlinkpath + '/' + name) + '\',\'' + encodeURIComponentEx(name) + '\',' + f.s + ')">' + shortname + '</a>';
9143 - // Server link
9144 - //link = '<a href="devicefile.ashx?c=' + authCookie + '&m=' + currentNode.meshid.split('/')[2] + '&n=' + currentNode._id.split('/')[2] + '&f=' + encodeURIComponentEx(newlinkpath + '/' + name) + '" download="' + name + '" style=cursor:pointer>' + shortname + '</a>';
9145 - // Server link
9146 - link = '<a onclick=downloadFile("devicefile.ashx?c=' + authCookie + '&m=' + currentNode.meshid.split('/')[2] + '&n=' + currentNode._id.split('/')[2] + '&f=' + encodeURIComponentEx(newlinkpath + '/' + name) + '","' + encodeURIComponentEx(name) + '") style=cursor:pointer>' + shortname + '</a>';
9141 + if (filesNode.mtype == 3) {
9142 + // Local link
9143 + link = '<a href=# style=cursor:pointer onclick="return p13downloadfile(\'' + encodeURIComponentEx(newlinkpath + '/' + name) + '\',\'' + encodeURIComponentEx(name) + '\',' + f.s + ')">' + shortname + '</a>';
9144 + } else {
9145 + // Server link
9146 + //link = '<a href="devicefile.ashx?c=' + authCookie + '&m=' + currentNode.meshid.split('/')[2] + '&n=' + currentNode._id.split('/')[2] + '&f=' + encodeURIComponentEx(newlinkpath + '/' + name) + '" download="' + name + '" style=cursor:pointer>' + shortname + '</a>';
9147 + // Server link
9148 + link = '<a onclick=downloadFile("devicefile.ashx?c=' + authCookie + '&m=' + currentNode.meshid.split('/')[2] + '&n=' + currentNode._id.split('/')[2] + '&f=' + encodeURIComponentEx(newlinkpath + '/' + name) + '","' + encodeURIComponentEx(name) + '") style=cursor:pointer>' + shortname + '</a>';
9149 + }
9150 }
9151 h = '<div id=fileEntry cmenu=filesContextMenu fileIndex=' + i + ' class=filelist file=3><input file=3 style=float:left name=fd class=fcb type=checkbox onchange=p13setActions() value=\'' + f.nx + '\'>&nbsp;<span class=fsize>' + fdatestr + '</span><span style=float:right>' + EscapeHtml(fsize) + '</span><span><div class=fileIcon' + f.t + '></div>' + link + '</span></div>';
9152 }