diff --git a/tinyfilemanager.php b/tinyfilemanager.php index 9dfa94cf9..71846e279 100644 --- a/tinyfilemanager.php +++ b/tinyfilemanager.php @@ -274,6 +274,15 @@ function session_error_handling_function($code, $msg, $file, $line) } } +// Parse JSON AJAX save requests before checking authentication +$contentType = isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : ''; +if (stripos($contentType, 'application/json') !== false) { + $jsonPost = json_decode(file_get_contents('php://input'), true); + if (is_array($jsonPost) && !empty($jsonPost['ajax']) && isset($jsonPost['type']) && $jsonPost['type'] === 'save') { + $_POST = $jsonPost; + } +} + if (empty($auth_users)) { $use_auth = false; } @@ -367,6 +376,12 @@ function getClientIP() } else { // Form unset($_SESSION[FM_SESSION_ID]['logged']); + if (!empty($_POST['ajax']) && isset($_POST['type']) && $_POST['type'] === 'save') { + header('HTTP/1.1 401 Unauthorized'); + header('Content-Type: text/plain; charset=UTF-8'); + header('Cache-Control: no-store'); + die("Session expired. The file was not saved. Log in again in another tab, then click Save again."); + } fm_show_header_login(); ?>
@@ -462,10 +477,6 @@ function getClientIP() // clean path $p = fm_clean_path($p); -// for ajax request - save -$input = file_get_contents('php://input'); -$_POST = (strpos($input, 'ajax') != FALSE && strpos($input, 'save') != FALSE) ? json_decode($input, true) : $_POST; - // instead globals vars define('FM_PATH', $p); define('FM_USE_AUTH', $use_auth); @@ -482,6 +493,13 @@ function getClientIP() // Handle all AJAX Request if ((isset($_SESSION[FM_SESSION_ID]['logged'], $auth_users[$_SESSION[FM_SESSION_ID]['logged']]) || !FM_USE_AUTH) && isset($_POST['ajax'], $_POST['token'])) { if (!verifyToken($_POST['token'])) { + if (isset($_POST['type']) && $_POST['type'] === 'save') { + header('HTTP/1.1 409 Conflict'); + header('Content-Type: text/plain; charset=UTF-8'); + header('Cache-Control: no-store'); + header('X-CSRF-Token: ' . $_SESSION['token']); + die("Invalid Token."); + } header('HTTP/1.0 401 Unauthorized'); die("Invalid Token."); } @@ -528,7 +546,10 @@ function getClientIP() header("HTTP/1.1 500 Internal Server Error"); die("Could Not Write File! - Check Permissions / Ownership"); } - die(true); + header('X-TFM-Save-Success: 1'); + header('Content-Type: text/plain; charset=UTF-8'); + header('Cache-Control: no-store'); + die('1'); } // backup files @@ -4942,16 +4963,31 @@ function edit_save(e, t) { url: window.location, data: JSON.stringify(data), contentType: "application/json; charset=utf-8", - success: function(mes) { - toast(""); - window.onbeforeunload = function() { - return + success: function(mes, textStatus, xhr) { + var saveConfirmed = xhr.getResponseHeader("X-TFM-Save-Success") === "1" || String(mes).trim() === "1"; + if (saveConfirmed) { + toast(""); + window.onbeforeunload = function() { + return + } + } else { + alert("The file was not saved. The server returned an unexpected response:\n\n" + String(mes).trim().substring(0, 500)); } }, failure: function(mes) { toast(""); }, error: function(mes) { + var refreshedToken = mes.getResponseHeader("X-CSRF-Token"); + if (mes.status === 409 && refreshedToken) { + window.csrf = refreshedToken; + alert("Your session was renewed. The file was not saved. The security token has been refreshed. Click Save again to confirm."); + return; + } + if (mes.status === 401) { + alert("Session expired. The file was not saved. Log in again in another tab, then click Save again."); + return; + } toast(`

${mes.responseText}

`); } });