false, 'error' => 'plugin disabled']); exit; } /** * Send a JSON response. * * @param int $status * @param array $payload * @return void */ function luxtools_scratchpad_json(int $status, array $payload): void { http_status($status); header('Content-Type: application/json; charset=utf-8'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Pragma: no-cache'); echo json_encode($payload); exit; } $cmd = (string)$INPUT->str('cmd'); $pad = (string)$INPUT->str('pad'); $pageId = (string)$INPUT->str('id'); if (function_exists('cleanID')) { $pageId = cleanID($pageId); } if ($cmd === '' || $pad === '' || $pageId === '') { luxtools_scratchpad_json(400, ['ok' => false, 'error' => 'missing parameters']); } // Require the user to at least be able to read the host page. if (function_exists('auth_quickaclcheck') && auth_quickaclcheck($pageId) < AUTH_READ) { luxtools_scratchpad_json(403, ['ok' => false, 'error' => 'forbidden']); } $map = new ScratchpadMap((string)$syntax->getConf('scratchpad_paths')); try { $resolved = (string)$map->resolve($pad); if ($resolved === '' || str_ends_with($resolved, '/')) { throw new Exception('Invalid scratchpad path'); } // Never allow writing/reading within DokuWiki-controlled paths. if (Path::isWikiControlled(Path::cleanPath($resolved, false))) { throw new Exception('Access to wiki files is not allowed'); } } catch (Exception $e) { luxtools_scratchpad_json(403, ['ok' => false, 'error' => 'access denied']); } if ($cmd === 'load') { $text = ''; $exists = @is_file($resolved); if ($exists) { if (!@is_readable($resolved)) { luxtools_scratchpad_json(500, ['ok' => false, 'error' => 'unreadable']); } $read = io_readFile($resolved, false); if ($read === false) { luxtools_scratchpad_json(500, ['ok' => false, 'error' => 'unreadable']); } $text = (string)$read; } luxtools_scratchpad_json(200, ['ok' => true, 'text' => $text]); } if ($cmd === 'save') { if (strtoupper($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') { luxtools_scratchpad_json(405, ['ok' => false, 'error' => 'method not allowed']); } // Require edit permission on the host page. if (function_exists('auth_quickaclcheck') && auth_quickaclcheck($pageId) < AUTH_EDIT) { luxtools_scratchpad_json(403, ['ok' => false, 'error' => 'forbidden']); } if (!checkSecurityToken()) { luxtools_scratchpad_json(403, ['ok' => false, 'error' => 'bad token']); } $text = (string)$INPUT->str('text'); // Ensure directory exists $dir = dirname($resolved); if (function_exists('io_mkdir_p')) { io_mkdir_p($dir); } elseif (!@is_dir($dir)) { @mkdir($dir, 0777, true); } $ok = false; if (function_exists('io_saveFile')) { $ok = (bool)io_saveFile($resolved, $text); } else { $ok = @file_put_contents($resolved, $text, LOCK_EX) !== false; } if (!$ok) { luxtools_scratchpad_json(500, ['ok' => false, 'error' => 'save failed']); } luxtools_scratchpad_json(200, ['ok' => true]); } luxtools_scratchpad_json(400, ['ok' => false, 'error' => 'unknown command']);