diff --git a/app/classes/web/upload_handler.py b/app/classes/web/upload_handler.py index 5ebcfff0..91c23d84 100644 --- a/app/classes/web/upload_handler.py +++ b/app/classes/web/upload_handler.py @@ -113,6 +113,12 @@ class UploadHandler(BaseHandler): self.do_upload = False if self.do_upload: + self.content_len = int(self.request.headers.get("Content-Length")) + if self.content_len > MAX_STREAMED_SIZE: + logger.error( + f"User with ID {user_id} attempted to upload a file that" + f" exceeded the max body size." + ) try: self.f = open(full_path, "wb") except Exception as e: diff --git a/app/frontend/templates/panel/server_files.html b/app/frontend/templates/panel/server_files.html index 87daf416..1095201e 100644 --- a/app/frontend/templates/panel/server_files.html +++ b/app/frontend/templates/panel/server_files.html @@ -9,14 +9,15 @@
- +
@@ -24,41 +25,51 @@
- {% include "parts/details_stats.html" %} + {% include "parts/details_stats.html" %}
- {% include "parts/server_controls_list.html" %} + {% include "parts/server_controls_list.html" %} -
-
- -
+
+
+ +
- - - - + + + - -
    -
  • -
    - - - - {{ translate('serverFiles', 'files', data['lang']) }} - -
    -
      -
    • {{ translate('serverFiles', 'loadingRecords', data['lang']) }}
    • - -
    -
  • -
-
- -
-

-
- {{ translate('serverFiles', 'editingFile', data['lang']) }} -
file_contents
-
-
- {{ translate('serverFiles', 'keybindings', data['lang']) }}: -
- - - - -
-

- -
+
    +
  • +
    + + + + {{ translate('serverFiles', 'files', data['lang']) }} + +
    +
      +
    • {{ translate('serverFiles', 'loadingRecords', data['lang']) + }}
    • + +
    +
  • +
+ +
+

+
+ {{ translate('serverFiles', 'editingFile', data['lang']) }} +
file_contents
+
+
+ {{ translate('serverFiles', 'keybindings', data['lang']) }}: +
+ + + + +
+

+ +
+
@@ -235,9 +265,9 @@ editor.session.setUseSoftTabs(true); // mouseup = css resize end - document.addEventListener("mouseup", function(e){ - editor.resize(); - }); + document.addEventListener("mouseup", function (e) { + editor.resize(); + }); let extensionChanges = [ { @@ -376,105 +406,105 @@ } } - var onlongtouch; + var onlongtouch; var timer; var touchduration = 500; //length of time we want the user to touch before we do something const longtouch = new Event('longtouch'); - + function touchstart(event) { - if (!timer) { - timer = setTimeout(onlongtouch, touchduration, event); - } + if (!timer) { + timer = setTimeout(onlongtouch, touchduration, event); + } } - + function touchend(event) { - //stops short touches from firing the event - if (timer) { - clearTimeout(timer); - console.log('Timer: ' + timer) - timer = null; - } + //stops short touches from firing the event + if (timer) { + clearTimeout(timer); + console.log('Timer: ' + timer) + timer = null; + } } - - onlongtouch = function(e) { - console.log('iOS long touch detected!'); - if([ - 'iPad Simulator', - 'iPhone Simulator', - 'iPod Simulator', - 'iPad', - 'iPhone', - 'iPod' - ].includes(navigator.platform) - // iPad on iOS 13 detection - || (navigator.userAgent.includes("Mac") && "ontouchend" in document)){ - e.preventDefault(); - e.stopImmediatePropagation(); - var ctxmenuPath = e.target.getAttribute('data-path'); - var ctxmenuName = e.target.getAttribute('data-name'); - if (!ctxmenuPath) { - console.log({ 'event.target': e.target, ctxmenuPath }); - return; - } - $('#renameItem').show(); - - var isDir = e.target.classList.contains('files-tree-title'); - $('#createFile').toggle(isDir); - $('#createDir').toggle(isDir); - $('#deleteDir').toggle(isDir); - $('#upload').toggle(isDir); - - var isFile = e.target.classList.contains('tree-file'); - $('#deleteFile').toggle(isFile); - $('#downloadFile').toggle(isFile); - console.log({ 'event.target': e.target, isDir, isFile }); - - if (e.target.classList.contains('root-dir')) { - $('#createFile').show(); - $('#createDir').show(); - $('#renameItem').hide(); - $('#deleteDir').hide(); - $('#deleteFile').hide(); - $('#downloadFile').hide(); - $('#upload').show(); - } - if (e.target.textContent.endsWith('.zip')) { - $('#unzip').show(); - console.log(e.target.textContent) - } else { - $('#unzip').hide(); - } - - var clientX = e.layerX + 2; - var clientY = e.layerY + 5; - - - - document.getElementById('files-tree-nav-content') - .setAttribute('data-path', ctxmenuPath); - document.getElementById('files-tree-nav-content') - .setAttribute('data-name', ctxmenuName); - document.getElementById("files-tree-nav").style.display = "flex"; - document.getElementById("files-tree-nav").style.position = "fixed"; - domRect = document.getElementById("files-tree-nav").getBoundingClientRect(); - sum = (clientY+domRect['height']) - window.innerHeight - if(domRect['height']+clientY > window.innerHeight){ - clientY = clientY - sum; - } - document.getElementById("files-tree-nav").style.top = clientY + 'px'; - document.getElementById("files-tree-nav").style.left = clientX + 'px'; - console.log(window.innerHeight) - timer = null; - }; + + onlongtouch = function (e) { + console.log('iOS long touch detected!'); + if ([ + 'iPad Simulator', + 'iPhone Simulator', + 'iPod Simulator', + 'iPad', + 'iPhone', + 'iPod' + ].includes(navigator.platform) + // iPad on iOS 13 detection + || (navigator.userAgent.includes("Mac") && "ontouchend" in document)) { + e.preventDefault(); + e.stopImmediatePropagation(); + var ctxmenuPath = e.target.getAttribute('data-path'); + var ctxmenuName = e.target.getAttribute('data-name'); + if (!ctxmenuPath) { + console.log({ 'event.target': e.target, ctxmenuPath }); + return; + } + $('#renameItem').show(); + + var isDir = e.target.classList.contains('files-tree-title'); + $('#createFile').toggle(isDir); + $('#createDir').toggle(isDir); + $('#deleteDir').toggle(isDir); + $('#upload').toggle(isDir); + + var isFile = e.target.classList.contains('tree-file'); + $('#deleteFile').toggle(isFile); + $('#downloadFile').toggle(isFile); + console.log({ 'event.target': e.target, isDir, isFile }); + + if (e.target.classList.contains('root-dir')) { + $('#createFile').show(); + $('#createDir').show(); + $('#renameItem').hide(); + $('#deleteDir').hide(); + $('#deleteFile').hide(); + $('#downloadFile').hide(); + $('#upload').show(); + } + if (e.target.textContent.endsWith('.zip')) { + $('#unzip').show(); + console.log(e.target.textContent) + } else { + $('#unzip').hide(); } - + var clientX = e.layerX + 2; + var clientY = e.layerY + 5; + + + + document.getElementById('files-tree-nav-content') + .setAttribute('data-path', ctxmenuPath); + document.getElementById('files-tree-nav-content') + .setAttribute('data-name', ctxmenuName); + document.getElementById("files-tree-nav").style.display = "flex"; + document.getElementById("files-tree-nav").style.position = "fixed"; + domRect = document.getElementById("files-tree-nav").getBoundingClientRect(); + sum = (clientY + domRect['height']) - window.innerHeight + if (domRect['height'] + clientY > window.innerHeight) { + clientY = clientY - sum; + } + document.getElementById("files-tree-nav").style.top = clientY + 'px'; + document.getElementById("files-tree-nav").style.left = clientX + 'px'; + console.log(window.innerHeight) + timer = null; + }; + } + + setFileName(); $('#editorParent').toggle(false) // show $('#fileError').toggle(false) // hide editor.blur() - function setMode (extension) { + function setMode(extension) { // if the extension matches with the RegEx it will return the replaceWith // property. else it will return the one it has. defaults to the extension. // this runs for each element in extensionChanges. @@ -506,14 +536,15 @@ var token = getCookie("_xsrf") $.ajax({ type: "PUT", - headers: {'X-XSRFToken': token}, + headers: { 'X-XSRFToken': token }, url: "/files/save_file?id=" + serverId, data: { file_contents: text, file_path: filePath }, - success: function(data){ + success: function (data) { console.log("got response:"); + //TODO add better feedback for file saves console.log(data); }, }); @@ -523,13 +554,13 @@ var token = getCookie("_xsrf") $.ajax({ type: "POST", - headers: {'X-XSRFToken': token}, + headers: { 'X-XSRFToken': token }, url: "/files/create_file?id=" + serverId, data: { file_parent: parent, file_name: name }, - success: function(data){ + success: function (data) { console.log("got response:"); console.log(data); callback(); @@ -541,13 +572,13 @@ var token = getCookie("_xsrf") $.ajax({ type: "POST", - headers: {'X-XSRFToken': token}, + headers: { 'X-XSRFToken': token }, url: "/files/create_dir?id=" + serverId, data: { dir_parent: parent, dir_name: name }, - success: function(data){ + success: function (data) { console.log("got response:"); console.log(data); callback(); @@ -559,13 +590,13 @@ var token = getCookie("_xsrf") $.ajax({ type: "PATCH", - headers: {'X-XSRFToken': token}, + headers: { 'X-XSRFToken': token }, url: "/files/rename_file?id=" + serverId, data: { item_path: path, new_item_name: name }, - success: function(data){ + success: function (data) { console.log("got response:"); console.log(data); callback(); @@ -578,12 +609,12 @@ var token = getCookie("_xsrf") $.ajax({ type: "DELETE", - headers: {'X-XSRFToken': token}, + headers: { 'X-XSRFToken': token }, url: "/files/del_file?id=" + serverId, data: { file_path: path }, - success: function(data){ + success: function (data) { console.log("got response:"); console.log(data); callback(); @@ -595,12 +626,12 @@ var token = getCookie("_xsrf") $.ajax({ type: "DELETE", - headers: {'X-XSRFToken': token}, + headers: { 'X-XSRFToken': token }, url: "/files/del_dir?id=" + serverId, data: { dir_path: path }, - success: function(data){ + success: function (data) { console.log("got response:"); console.log(data); callback(); @@ -612,7 +643,7 @@ var token = getCookie("_xsrf") $.ajax({ type: "POST", - headers: {'X-XSRFToken': token}, + headers: { 'X-XSRFToken': token }, url: "/files/unzip_file?id=" + serverId, data: { path: path @@ -621,16 +652,18 @@ window.location.href = "/panel/server_detail?id=" + serverId + "&subpage=files" } - function sendFile(file, path, serverId, left, onProgress){ - var xmlHttpRequest = new XMLHttpRequest(); - var token = getCookie("_xsrf") - var fileName = file.name - var target = '/upload?server_id=' + serverId - var mimeType = file.type + function sendFile(file, path, serverId, left, onProgress) { + let xmlHttpRequest = new XMLHttpRequest(); + let token = getCookie("_xsrf") + let fileName = file.name + let target = '/upload?server_id=' + serverId + let mimeType = file.type + let size = file.size xmlHttpRequest.open('POST', target, true); xmlHttpRequest.setRequestHeader('X-Content-Type', mimeType); xmlHttpRequest.setRequestHeader('X-XSRFToken', token); + xmlHttpRequest.setRequestHeader('X-Content-Length', size); xmlHttpRequest.setRequestHeader('X-Content-Disposition', 'attachment; filename="' + fileName + '"'); xmlHttpRequest.setRequestHeader('X-Path', path); xmlHttpRequest.setRequestHeader('X-Files-Left', left); @@ -649,52 +682,65 @@ }, false); xmlHttpRequest.addEventListener('error', (e) => { console.error('Error while uploading file', file.name + '.', 'Event:', e) + var dialog = bootbox.dialog({ + title: 'Error on Upload', + message: "Fix size exceeded the max body size. Contact your system administrator.", + size: 'large', + buttons: { + ok: { + label: "Ok", + className: 'btn-info', + callback: function () { + document.location.reload(); + } + } + } + }); }, false); - xmlHttpRequest.send(file); } - var uploadWaitDialog; - var doUpload = true; + let uploadWaitDialog; + let doUpload = true; - function uploadFilesE(event){ + function uploadFilesE(event) { path = event.target.parentElement.getAttribute('data-path'); console.log("PATH: " + path); $(function () { var uploadHtml = "
" + - '
'+"
"; + "
"; bootbox.dialog({ message: uploadHtml, - title: "{{ translate('serverFiles', 'uploadTitle', data['lang'])}}"+path, + title: "{{ translate('serverFiles', 'uploadTitle', data['lang'])}}" + path, buttons: { success: { label: "{{ translate('serverFiles', 'upload', data['lang']) }}", className: "btn-default", callback: async function () { - var height = files.files.length*50; + var height = files.files.length * 50; - var waitMessage = '

'+ - ''+ - "{{ translate('serverFiles', 'waitUpload', data['lang']) }}"+'
'+ - ''+"{{ translate('serverFiles', 'stayHere', data['lang']) }}"+''+ - '

'+ - '
'+ - '
' - files = document.getElementById("files"); - uploadWaitDialog = bootbox.dialog({ - message: waitMessage, - closeButton: false + var waitMessage = '

' + + '' + + "{{ translate('serverFiles', 'waitUpload', data['lang']) }}" + '
' + + '' + "{{ translate('serverFiles', 'stayHere', data['lang']) }}" + '' + + '

' + + '
' + + '
' + files = document.getElementById("files"); + uploadWaitDialog = bootbox.dialog({ + message: waitMessage, + closeButton: false }); let nFiles = files.files.length; - for(i=0; i < files.files.length; i++) { + for (i = 0; i < files.files.length; i++) { if (!doUpload) { doUpload = true; hideUploadBox(); @@ -731,12 +777,12 @@ var fileList = document.getElementById("files"); fileList.addEventListener("change", function (e) { - var list = ""; - for (var i = 0; i < this.files.length; i++) { - list += "
  • " + this.files[i].name + "
  • " - } + var list = ""; + for (var i = 0; i < this.files.length; i++) { + list += "
  • " + this.files[i].name + "
  • " + } - document.getElementById("fileList").innerHTML = list; + document.getElementById("fileList").innerHTML = list; }, false); }); } @@ -748,7 +794,7 @@ type: "GET", url: "/files/get_tree?id=" + serverId + "&path=" + path, dataType: 'text', - success: function(data){ + success: function (data) { console.log("got response:"); console.log(data); @@ -756,10 +802,10 @@ serverDir = dataArr.shift(); // Remove & return first element (server directory) text = dataArr.join('\n'); - try{ + try { document.getElementById(path).innerHTML += text; event.target.parentElement.classList.add("clicked"); - }catch{ + } catch { document.getElementById('files-tree').innerHTML = text; } @@ -767,28 +813,28 @@ document.getElementsByClassName('files-tree-title')[0].setAttribute('data-path', serverDir); document.getElementsByClassName('files-tree-title')[0].setAttribute('data-name', 'Files'); - setTimeout(function () {setTreeViewContext()}, 1000); + setTimeout(function () { setTreeViewContext() }, 1000); }, }); } function getToggleMain(event) { path = event.target.parentElement.getAttribute('data-path'); - document.getElementById("files-tree").classList.toggle("d-block"); - document.getElementById(path+"span").classList.toggle("tree-caret-down"); - document.getElementById(path+"span").classList.toggle("tree-caret"); - } + document.getElementById("files-tree").classList.toggle("d-block"); + document.getElementById(path + "span").classList.toggle("tree-caret-down"); + document.getElementById(path + "span").classList.toggle("tree-caret"); + } function getDirView(event) { let path = event.target.parentElement.getAttribute('data-path'); if (document.getElementById(path).classList.contains('clicked')) { - var toggler = document.getElementById(path+"span"); + var toggler = document.getElementById(path + "span"); if (toggler.classList.contains('files-tree-title')) { - document.getElementById(path+"ul").classList.toggle("d-block"); - document.getElementById(path+"span").classList.toggle("tree-caret-down"); + document.getElementById(path + "ul").classList.toggle("d-block"); + document.getElementById(path + "span").classList.toggle("tree-caret-down"); } return; } else { @@ -796,7 +842,7 @@ type: "GET", url: "/files/get_dir?id=" + serverId + "&path=" + path, dataType: 'text', - success: function(data) { + success: function (data) { console.log("got response:"); dataArr = data.split('\n'); @@ -804,21 +850,21 @@ text = dataArr.join('\n'); try { - document.getElementById(path+"span").classList.add('tree-caret-down'); + document.getElementById(path + "span").classList.add('tree-caret-down'); document.getElementById(path).innerHTML += text; document.getElementById(path).classList.add("clicked"); } catch { console.log("Bad") } - setTimeout(function () {setTreeViewContext()}, 1000); + setTimeout(function () { setTreeViewContext() }, 1000); var toggler = document.getElementById(path); - if (toggler.classList.contains('files-tree-title')){ - document.getElementById(path+"span").addEventListener("click", function caretListener() { - document.getElementById(path+"ul").classList.toggle("d-block"); - document.getElementById(path+"span").classList.toggle("tree-caret-down"); + if (toggler.classList.contains('files-tree-title')) { + document.getElementById(path + "span").addEventListener("click", function caretListener() { + document.getElementById(path + "ul").classList.toggle("d-block"); + document.getElementById(path + "span").classList.toggle("tree-caret-down"); }); } }, @@ -831,19 +877,19 @@ for (var i = 0; i < treeItems.length; i++) { var treeItem = treeItems[i]; - if([ - 'iPad Simulator', - 'iPhone Simulator', - 'iPod Simulator', - 'iPad', - 'iPhone', - 'iPod' - ].includes(navigator.platform) + if ([ + 'iPad Simulator', + 'iPhone Simulator', + 'iPod Simulator', + 'iPad', + 'iPhone', + 'iPod' + ].includes(navigator.platform) // iPad on iOS 13 detection - || (navigator.userAgent.includes("Mac") && "ontouchend" in document)){ + || (navigator.userAgent.includes("Mac") && "ontouchend" in document)) { treeItem.addEventListener("touchstart", touchstart, false); treeItem.addEventListener("touchend", touchend, false); - } + } treeItem.addEventListener('contextmenu', function contextListener(event) { event.preventDefault(); var ctxmenuPath = event.target.getAttribute('data-path'); @@ -881,8 +927,8 @@ $('#unzip').hide(); } - var clientX = event.clientX; - var clientY = event.clientY; + var clientX = event.clientX; + var clientY = event.clientY; @@ -893,19 +939,19 @@ document.getElementById("files-tree-nav").style.display = "flex"; document.getElementById("files-tree-nav").style.position = "fixed"; domRect = document.getElementById("files-tree-nav").getBoundingClientRect(); - sum = (clientY+domRect['height']) - window.innerHeight - if(domRect['height']+clientY > window.innerHeight){ + sum = (clientY + domRect['height']) - window.innerHeight + if (domRect['height'] + clientY > window.innerHeight) { clientY = clientY - sum } document.getElementById("files-tree-nav").style.top = clientY + 'px'; - document.getElementById("files-tree-nav").style.left = clientX + 'px'; + document.getElementById("files-tree-nav").style.left = clientX + 'px'; console.log(domRect) console.log(window.innerHeight) }) } } - document.addEventListener('click', function(e){ + document.addEventListener('click', function (e) { let inside = (e.target.closest('#files-tree-nav')); let contextMenu = document.getElementById('files-tree-nav'); if (!inside) { @@ -920,14 +966,14 @@ hideUploadBox(); }); } - function hideUploadBox(){ + function hideUploadBox() { if (!uploadWaitDialog) return; uploadWaitDialog.modal('hide'); getTreeView(); } function createFileE(event) { - bootbox.prompt("{% raw translate('serverFiles', 'createFileQuestion', data['lang']) %}", function(result) { + bootbox.prompt("{% raw translate('serverFiles', 'createFileQuestion', data['lang']) %}", function (result) { path = event.target.parentElement.getAttribute('data-path'); name = event.target.parentElement.getAttribute('data-name'); if (!result) return; @@ -940,7 +986,7 @@ } function createDirE(event) { - bootbox.prompt("{% raw translate('serverFiles', 'createDirQuestion', data['lang']) %}", function(result) { + bootbox.prompt("{% raw translate('serverFiles', 'createDirQuestion', data['lang']) %}", function (result) { path = event.target.parentElement.getAttribute('data-path'); name = event.target.parentElement.getAttribute('data-name'); if (!result) return; @@ -963,7 +1009,7 @@ bootbox.prompt({ title: "{% raw translate('serverFiles', 'renameItemQuestion', data['lang']) %}", value: name, - callback: function(result) { + callback: function (result) { if (!result) return; renameItem(path, result, function () { getTreeView() @@ -972,8 +1018,8 @@ } }); } - function unzipFilesE(event) { - path = event.target.parentElement.getAttribute('data-path'); + function unzipFilesE(event) { + path = event.target.parentElement.getAttribute('data-path'); unZip(path) } @@ -981,27 +1027,27 @@ path = event.target.parentElement.getAttribute('data-path'); name = event.target.parentElement.getAttribute('data-name'); bootbox.confirm({ - size: "", - title: "{% raw translate('serverFiles', 'deleteItemQuestion', data['lang']) %}", - closeButton: false, - message: "{% raw translate('serverFiles', 'deleteItemQuestionMessage', data['lang']) %}", - buttons: { - confirm: { - label: "{{ translate('serverFiles', 'yesDelete', data['lang']) }}", - className: 'btn-danger' - }, - cancel: { - label: "{{ translate('serverFiles', 'noDelete', data['lang']) }}", - className: 'btn-link' - } + size: "", + title: "{% raw translate('serverFiles', 'deleteItemQuestion', data['lang']) %}", + closeButton: false, + message: "{% raw translate('serverFiles', 'deleteItemQuestionMessage', data['lang']) %}", + buttons: { + confirm: { + label: "{{ translate('serverFiles', 'yesDelete', data['lang']) }}", + className: 'btn-danger' }, - callback: function(result) { - if (!result) return; - deleteFile(path, function () { - getTreeView() - document.getElementById('files-tree-nav').style.display = 'none'; - }); + cancel: { + label: "{{ translate('serverFiles', 'noDelete', data['lang']) }}", + className: 'btn-link' } + }, + callback: function (result) { + if (!result) return; + deleteFile(path, function () { + getTreeView() + document.getElementById('files-tree-nav').style.display = 'none'; + }); + } }); } @@ -1023,7 +1069,7 @@ className: 'btn-link' } }, - callback: function(result) { + callback: function (result) { if (!result) return; deleteDir(path, function () { getTreeView() @@ -1055,4 +1101,4 @@ -{% end %} +{% end %} \ No newline at end of file