diff options
author | sss <sss@dark-alexandr.net> | 2023-01-17 00:38:19 +0300 |
---|---|---|
committer | sss <sss@dark-alexandr.net> | 2023-01-17 00:38:19 +0300 |
commit | cc3f33db7a8d3c4ad373e646b199808e01bc5d9b (patch) | |
tree | ec09d690c7656ab5f2cc72607e05fb359c24d8b2 /www/js/clipboard.js |
added webrdp public code
Diffstat (limited to 'www/js/clipboard.js')
-rw-r--r-- | www/js/clipboard.js | 535 |
1 files changed, 535 insertions, 0 deletions
diff --git a/www/js/clipboard.js b/www/js/clipboard.js new file mode 100644 index 0000000..77ea154 --- /dev/null +++ b/www/js/clipboard.js @@ -0,0 +1,535 @@ +'use strict'; +/** BSD-2-Clause license + * + * Copyright (c) 2018-2023 NST <www.newinfosec.ru>, sss <sss at dark-alexandr dot net>, smake <smake at ya dot ru>. + * + */ + +/** + * Main clipboard class, that dispatch clipboard events and handle their status + */ + + +class ClipboardHandler { + constructor(rdp) { + this.rdp = rdp; + this.sock = this.rdp.sock; + this.filelistIn = []; + this.filelistOut = []; + this.textIn = null; + this.textOut = null; + this.update(); + return this; + } + + get istransferring() { + /* + * check for any file is currently transferring + * returns strings "in", "out" or bool false + */ + if (this.filelistOut.length > 0) { + for (var i = 0, f; f = this.filelistOut[i]; i++) { + if (f.isTransferring){ + return "out"; + } + } + } + if (this.filelistIn.length > 0) { + for (var i = 0, f; f = this.filelistIn[i]; i++) { + if (f.isTransferring){ + return "in"; + } + } + } + return false; + } + + ClipoboardFileGet(fid) { + var ft = this.filelistIn[fid]; + if (!ft) { + return; + } + if (this.istransferring) { + console.log("Transfer in progress, please await"); + return; + } + if (this.sock.readyState == this.sock.OPEN) { + this.rdp.log.debug('requesting file transfer', 1); + var buf = ft.nextChunkRequest; + // console.log(buf.toHexdump()); + this.sock.send(buf); + }; + + } + + ClipoboardSendFilelist(files) { + if (!files.length) { + console.log("There in now escape(files)") + return; + } + if (this.istransferring) { + var msg = lables.files_in_transfer; + this.rdp.fireEvent('alert', msg, 2); + console.log("Transfer in progress, please await"); + return; + } + this.filelistOut = []; + this.filelistIn = []; + for (var i = 0, f; f = files[i]; i++) { + /* + * Bug in microsoft RDP server + * https://support.microsoft.com/en-us/help/2258090/copying-files-larger-than-2-gb-over-a-remote-desktop-services-or-termi + */ + if (f.size < 2147483647) { + var j = this.filelistOut.length + var ft_entry = new FileEntry(this.rdp,f,j); + this.filelistOut[j] = ft_entry; + this.checkFileReadable(ft_entry); + } else { + var msg = f.name + ": " + lables.file_lager_then_2g; + this.rdp.fireEvent('alert', msg, 2); + } + } + + if (this.filelistOut.length == 0) { + // popUpMessage + console.log("There in now escape(files)") + return; + } + + if (this.sock.readyState == this.sock.OPEN) { + this.rdp.log.debug('updating remote clipbord status', 1); + var buf = new ArrayBuffer(6); + var c = new Uint32Array(buf, 0, 1); + var count = new Uint8Array(buf, 4, 1); + var fmt = new Uint8Array(buf, 5, 1); + c[0] = 6; // ws_in_clipbrd_changed + count[0] = this.filelistOut.length; + fmt[0] = 3; + // console.log(buf.toHexdump()); + this.sock.send(buf); + }; + this.update(); + + } + + getFile() { + if (this.rdp.sock.readyState == this.rdp.sock.OPEN) { + this.rdp.log.debug('requesting file transfer', 1); + var buf = this.nextChunkRequest; + console.log(buf.toHexdump()); + this.rdp.sock.send(buf); + }; + } + + resetUi() { + var cbdinimg = document.querySelector("div#clipboardin > img"); + cbdinimg.style.backgroundColor = "#eee"; + cbdinimg.src = "images/cbd_empty.png"; + + var new_element = cbdinimg.cloneNode(true); + cbdinimg.parentNode.replaceChild(new_element, cbdinimg); + var cbdinimg = new_element; + + var cbdoutimg = document.querySelector("div#clipboardout > img"); + cbdoutimg.style.backgroundColor = "#eee"; + cbdoutimg.src = "images/cbd_empty_out.png"; + + var new_element = cbdoutimg.cloneNode(true); + cbdoutimg.parentNode.replaceChild(new_element, cbdoutimg); + var cbdoutimg = new_element; + + var pos = this.rdp.canvas.getPosition(); + var size = this.rdp.canvas.getSize(); + + var cbdcontent = document.getElementById('cbdcontent'); + cbdcontent.innerHTML = ""; + cbdcontent.setStyle('max-height', size.y); + cbdcontent.setStyle('overflow-y', "auto"); + cbdcontent.style.visibility = "hidden"; + } + + update() { + /* + * Re-draw + */ + + this.resetUi(); + var cbdinimg = document.querySelector("div#clipboardin > img"); + var cbdoutimg = document.querySelector("div#clipboardout > img"); + var cbdcontent = document.getElementById('cbdcontent'); + cbdcontent.addEventListener("mouseleave", function(evt) { + evt.target.style.visibility = "hidden"; + }); + + + cbdoutimg.cbd = this; + cbdoutimg.addEventListener("click", function(evt) { + var cbdcontent = document.getElementById('cbdcontent'); + var cbd = evt.target.cbd; + if (cbd.istransferring) { + return; + } + cbd.filelistIn = []; + cbd.filelistOut = []; + cbd.textIn = null; + cbd.textOut = null; + cbd.update(); + navigator.clipboard.readText().then(text => { + rdp.cbd.textOut = text; + rdp.cbd.update(); + }).catch(err => { + cbdcontent.style.visibility = "visible"; + var input = document.createElement('input'); + cbdcontent.appendChild(input); + input.cbd = cbd; + input.addEventListener("paste", function(evt) { + var cbd = evt.target.cbd; + cbd.textOut = "" + evt.clipboardData.getData("text"); + cbd.update(); + }); + input.focus(); + console.error('Failed to read clipboard contents: ', err); + }); + + }); + + if (this.filelistIn.length > 0) { + if (this.istransferring == "in") { + cbdinimg.src = "images/cbd_wait.png"; + } else { + cbdinimg.src = "images/cbd_files.png"; + } + + this.iconAddListners(cbdinimg); + + for (var i =0; i < this.filelistIn.length; i++) { + var file = this.filelistIn[i]; + cbdcontent.innerHTML += file.uiElement; + } + + return; + } else if (this.filelistOut.length > 0) { + if (this.istransferring == "out") { + cbdoutimg.src = "images/cbd_wait.png"; + } else { + cbdoutimg.src = "images/cbd_files.png"; + } + + this.iconAddListners(cbdoutimg); + + var html = ""; + for (var i =0; i < this.filelistOut.length; i++) { + var file = this.filelistOut[i]; + cbdcontent.innerHTML += file.uiElement + } + } else if (this.textIn) { + cbdinimg.src = "images/cbd_text.png"; + cbdoutimg.src = "images/cbd_empty_out.png"; + + this.iconAddListners(cbdinimg); + + cbdcontent.innerHTML = '<input type="text" value="' + this.textIn + '">'; + } else if (this.textOut) { + cbdinimg.src = "images/cbd_empty.png"; + cbdoutimg.src = "images/cbd_text.png"; + + this.iconAddListners(cbdoutimg); + if (this.sock.readyState == this.sock.OPEN) { + this.rdp.log.debug('updating remote clipbord status', 1); + var buf = new ArrayBuffer(6); + var c = new Uint32Array(buf, 0, 1); + var count = new Uint8Array(buf, 4, 1); + var fmt = new Uint8Array(buf, 5, 1); + c[0] = 6; // ws_in_clipbrd_changed + count[0] = 1; + fmt[0] = 2; + // console.log(buf.toHexdump()); + this.sock.send(buf); + }; + + cbdcontent.innerHTML = '<input type="text" value="' + this.textOut + '">'; + } else { + cbdinimg.src = "images/cbd_empty.png"; + cbdoutimg.src = "images/cbd_empty_out.png"; + } + + } + + iconAddListners (obj) { + var cbdcontent = document.getElementById('cbdcontent'); + obj.addEventListener("mouseover", function(evt) { + var cbdcontent = document.getElementById('cbdcontent'); + cbdcontent.style.visibility = "visible"; + evt.target.style.backgroundColor = "darkgreen"; + }); + obj.addEventListener("mouseout", function(evt) { + var fl = document.getElementById('cbdcontent'); + evt.target.style.backgroundColor = "#eee"; + }); + + } + + ClipbrdInfoParse(fmts) { + var clipboardimg = document.querySelector("div#clipboardin > img"); + if (this.istransferring) { + console.log("Transfer in progress, please await"); + return; + } + var fmt = 0; + if (fmts.length > 1) { + for (var i = 0; i < fmts.length; ++i) { + if (fmts[i] == 3) { + fmt = 3; + } + } + } + clipboardimg.style.backgroundColor = "darkgreen"; + switch (fmt) { + case 0: // clip_format_raw + case 1: // clip_format_text + case 2: // clip_format_utf8 + if (this.sock.readyState == this.sock.OPEN) { + this.rdp.log.debug('getting raw buffer', 1); + var buf = new ArrayBuffer(5); + var c = new Uint32Array(buf, 0, 1); + var t = new Uint8Array(buf, 4, 1); + c[0] = 8; // ws_in_clipbrd_data_request + t[0] = 2; + this.sock.send(buf); + clipboardimg.src = "images/cbd_text.png"; + setTimeout(function () { + clipboardimg.style.backgroundColor = "#eee"; + }, 100); + }; + break; + case 3: // clip_format_FileGroupDescriptorW + if (this.sock.readyState == this.sock.OPEN) { + this.rdp.log.debug('getting file list', 1); + var buf = new ArrayBuffer(5); + var c = new Uint32Array(buf, 0, 1); + var t = new Uint8Array(buf, 4, 1); + c[0] = 8; // ws_in_clipbrd_data_request + t[0] = 3; + this.sock.send(buf); + clipboardimg.src = "images/cbd_files.png"; + setTimeout(function () { + clipboardimg.style.backgroundColor = "#eee"; + }, 100); + }; + break; + default: + this.innerHTML = lables.cbd_empty; + break; + } + return true; + } + + sendClipboardInfo (type) { + if (this.istransferring) { + console.log("Transfer in progress, please await"); + return; + } + switch(type) + { + case 0: + case 1: + case 2: + { + if (this.sock.readyState != this.sock.OPEN) { + break; + } + // if (!this.textOut) { + // return; + // } + var text = new TextEncoder().encode(this.textOut); + + // var buf = new ArrayBuffer(4 + 1 + 4 + text.byteLength); + var buf = new ArrayBuffer(4 + 1 + text.byteLength); + var bufArr = new Uint8Array(buf); + var c = new Uint32Array(buf, 0, 1); + var type = new Uint8Array(buf, 4, 1); + // var size = new Uint8Array(buf, 5, 1); + var data = new Uint8Array(buf, 5); + var tmpSize = new Uint32Array(1); + c[0] = 7; // ws_in_clipbrd_data + type[0] = 2; + tmpSize[0] = text.byteLength + // size.set(tmpSize); + data.set(text); + console.log(buf.toHexdump()); + this.sock.send(buf); + } + break; + case 3: + { + + if (this.sock.readyState != this.sock.OPEN) { + break; + } + if(this.filelistOut.length == 0) { + console.log("no files in buffer"); + break; + } + var ftlenght = 0; + for (var i =0; i < this.filelistOut.length; i++) { + var f = this.filelistOut[i]; + ftlenght += f.raw_ft.length; + }; + + var ftOffset = 4 + 2 + 1; + var buf = new ArrayBuffer(ftOffset + ftlenght); + var bufArr = new Uint8Array(buf); + var c = new Uint32Array(buf, 0, 1); + var type = new Uint8Array(buf, 4, 1); + var count = new Uint16Array(1); + c[0] = 7; // ws_in_clipbrd_data + type[0] = 3; + count[0] = this.filelistOut.length; + + // because of buf migth be not multiple Uint16Array, which throw exeption in js + bufArr.set(new Uint8Array(count.buffer), 4 + 1); + + for (var i =0; i < this.filelistOut.length; i++) { + var f = this.filelistOut[i]; + bufArr.set(f.raw_ft, ftOffset); + ftOffset += f.raw_ft.length; + }; + + // console.log(buf.toHexdump()); + this.sock.send(buf); + } + break; + default: + break; + } + } + + reciveClipboardInfo(data) { + /* type is: + * typedef enum + * { + * clip_format_unsupported = -1, + * clip_format_raw, + * clip_format_text, + * clip_format_unicode, + * clip_format_file_list + * }wrdp_enum_clip_format; + * NOTE: must be kept in sync with core + */ + if (this.istransferring) { + console.log("Transfer in progress, please await"); + return; + } + var type = new Uint8Array(data, 4, 1); + this.filelistIn = []; + this.filelistOut = []; + this.textIn = null; + this.textOut = null; + switch(type[0]) + { + case 0: + case 1: + { + /* text is most probably in local rdp server + * endocing */ + var buf = new Uint8Array(data, 5); + //var text = new TextDecoder("utf-8").decode(buf); + //Viva la hardcoding encoding + var text = new TextDecoder("windows-1251").decode(buf.filter(v => v != 0)); + this.textIn = text; + navigator.clipboard.writeText(text).then(function() { + /* clipboard successfully set */ + // console.log("buffer setted") + return; + }, function() { + /* clipboard write failed */ + var msg = lables.files_in_transfer; + this.rdp.fireEvent('alert', msg, 2); + console.log("buffer not setted") + return; + }); + this.update(); + } + break; + case 2: + { + var buf = new Uint8Array(data, 5); + var text = new TextDecoder("utf-8").decode(buf.filter(v => v != 0)); + this.textIn = text; + navigator.clipboard.writeText(text).then(function() { + /* clipboard successfully set */ + // console.log("buffer setted"); + return; + }, function() { + /* clipboard write failed */ + console.log("buffer not setted") + return; + }); + this.update(); + } + break; + case 3: + { + /* + * typedef struct + * { + * uint16_t filename_len; + * uint32_t file_id; + * uint64_t file_size, file_offset; + * }wrdp_backend_ft_list_entry; + * NOTE: must be kept in sync with core + * NOTE: file_offset is omited here + */ + var count = new Uint16Array(data.slice(5,7)); + var list = data.slice(7); + var pos = 0; + for (var i = 0; i < count[0]; ++i) + { + var name_len = new Uint16Array(list.slice(pos, pos + 2)); + var entry_size = 2 + 4 + 8 + name_len[0] + var id = new Uint32Array(list.slice(pos + 2, pos + 2 + 4))[0]; + var ft_entry = new Uint8Array(list.slice(pos, pos + entry_size)); + /* "name" in utf8 encoding */ + this.filelistIn[id] = new FileEntry(this.rdp, ft_entry); + pos += entry_size; + this.update(); + } + } + break; + default: + break; + } + + } + + checkFileReadable(fe) { + var offset = 0; + var req_size = 8; + + var blob = fe.file.slice(offset, offset + req_size); + var reader = new FileReader(); + reader.fe = fe; + reader.onerror = function(e) { + var file = e.target.fe; + var msg = file.name + ": " + e.target.error.message; + file.rdp.cbd.filelistOut.pop(file); + file.rdp.fireEvent('alert', msg); + file.rdp.cbd.update(); + if (file.rdp.sock.readyState == file.rdp.sock.OPEN) { + file.rdp.log.debug('updating remote clipbord status', 1); + var buf = new ArrayBuffer(6); + var c = new Uint32Array(buf, 0, 1); + var count = new Uint8Array(buf, 4, 1); + var fmt = new Uint8Array(buf, 5, 1); + c[0] = 6; // ws_in_clipbrd_changed + count[0] = file.rdp.cbd.filelistOut.length; + fmt[0] = 3; + // console.log(buf.toHexdump()); + file.rdp.sock.send(buf); + }; + }; + reader.readAsArrayBuffer(blob); + } + +} |