127 lines
3.6 KiB
PHP
127 lines
3.6 KiB
PHP
<?php
|
|
|
|
// phpcs:disable PSR1.Files.SideEffects.FoundWithSymbols
|
|
|
|
use dokuwiki\plugin\luxtools\Path;
|
|
use dokuwiki\plugin\luxtools\ScratchpadMap;
|
|
|
|
if (!defined('DOKU_INC')) define('DOKU_INC', __DIR__ . '/../../../');
|
|
require_once(DOKU_INC . 'inc/init.php');
|
|
|
|
global $INPUT;
|
|
|
|
$syntax = plugin_load('syntax', 'luxtools');
|
|
if (!$syntax) {
|
|
http_status(500);
|
|
header('Content-Type: application/json');
|
|
echo json_encode(['ok' => 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']);
|