Add file & substitution_pipe support.

Also use nomnoml in SVG mode instead of nomnoml-cli, because the latter 
is a pain to build.
This commit is contained in:
Starbeamrainbowlabs 2019-10-24 20:13:03 +01:00
parent ec85f80b35
commit b75ad067eb
Signed by: sbrl
GPG Key ID: 1BE5172E637709C2
3 changed files with 88 additions and 30 deletions

View File

@ -337,7 +337,7 @@
"version": "0.10",
"author": "Emanuil Rusev & Starbeamrainbowlabs",
"description": "An upgraded (now default!) parser based on Emanuil Rusev's Parsedown Extra PHP library (https:\/\/github.com\/erusev\/parsedown-extra), which is licensed MIT. Please be careful, as this module adds some weight to your installation.",
"lastupdate": 1571614692,
"lastupdate": 1571944258,
"optional": false,
"extra_data": {
"Parsedown.php": "https:\/\/raw.githubusercontent.com\/erusev\/parsedown\/fe7a50eceb4a3c867cc9fa9c0aa906b1067d1955\/Parsedown.php",

View File

@ -110,36 +110,85 @@ register_module([
exit();
}
$source_handle = tmpfile();
fwrite($source_handle, $source);
fseek($source_handle, 0);
// Create the cache directory if doesn't exist already
if(!file_exists(dirname($cache_file_location)))
mkdir(dirname($cache_file_location), 0750, true);
$dest_handle = fopen($cache_file_location, "wb+");
$cli_to_execute = $renderer->cli;
$error_text_handle = tmpfile();
$descriptors = [
0 => null, // stdin
1 => null, // stdout
2 => tmpfile() // stderr
];
switch ($renderer->cli_mode) {
case "pipe":
// Fill stdin with the input text
$descriptors[0] = tmpfile();
fwrite($descriptors[0], $source);
fseek($descriptors[0], 0);
// Pipe the output to be the cache file
$descriptors[1] = fopen($cache_file_location, "wb+");
break;
case "substitution_pipe":
// Update the command that we're going to execute
$cli_to_execute = str_replace(
"{input_text}",
escapeshellarg($source),
$cli_to_execute
);
// Set the descriptors
$descriptors[0] = tmpfile();
$descriptors[1] = fopen($cache_file_location, "wb+");
break;
case "file":
$descriptors[0] = tmpfile();
fwrite($descriptors[0], $source);
$descriptors[1] = tmpfile();
$cli_to_execute = str_replace(
[ "{input_file}", "{output_file}" ],
[
escapeshellarg(stream_get_meta_data($descriptors[0])["uri"]),
escapeshellarg($cache_file_location)
],
$cli_to_execute
);
break;
default:
http_response_code(503);
header("cache-control: no-cache, no-store, must-revalidate");
header("content-type: image/png");
imagepng(errorimage("Error: Unknown external renderer mode '$renderer->cli_mode'.\nPlease contact $settings->admindetails_name, $settings->sitename's administrator."));
exit();
break;
}
if('\\' !== DIRECTORY_SEPARATOR) {
// We're not on Windows, so we can use timeout to force-kill if it takes too long
$cli_to_execute = "timeout {$settings->parser_ext_time_limit} $cli_to_execute";
}
$start_time = microtime(true);
$process_handle = proc_open(
$renderer->cli,
[
0 => $source_handle,
1 => $dest_handle,
2 => $error_text_handle
],
$cli_to_execute,
$descriptors,
$pipes,
null, // working directory
null // environment variables
);
if(!is_resource($process_handle)) {
fclose($dest_handle);
fclose($source_handle);
fclose($error_text_handle);
fclose($descriptors[0]);
fclose($descriptors[1]);
fclose($descriptors[2]);
unlink($cache_file_location);
if(file_exists($cache_file_location)) unlink($cache_file_location);
http_response_code(503);
header("cache-control: no-cache, no-store, must-revalidate");
@ -150,22 +199,22 @@ register_module([
// Wait for it to exit
$exit_code = proc_close($process_handle);
fclose($dest_handle);
fclose($source_handle);
fclose($descriptors[0]);
fclose($descriptors[1]);
$time_taken = round((microtime(true) - $start_time) * 1000, 2);
if($exit_code !== 0) {
fseek($error_text_handle, 0);
$error_details = stream_get_contents($error_text_handle);
if($exit_code !== 0 || !file_exists($cache_file_location)) {
fseek($descriptors[2], 0);
$error_details = stream_get_contents($descriptors[2]);
// Delete the cache file, which is guaranteed to exist because
// we pre-emptively create it above
unlink($cache_file_location);
if(file_exists($cache_file_location)) unlink($cache_file_location);
http_response_code(503);
header("content-type: image/png");
imagepng(errorimage(
"Error: The external renderer ($renderer->name)\nexited with code $exit_code.\nDetails:\n" . wordwrap($error_details)
"Error: The external renderer ($renderer->name)\nexited with code $exit_code,\nor potentially did not create the output file.\nDetails:\n" . wordwrap($error_details)
));
exit();
}

View File

@ -22,14 +22,14 @@
"parser_cache": { "type": "checkbox", "description": "Whether parser output should be cached to speed things up. The cache directory is <code>._cache</code> in the data directory - delete it if you experience issues (unlikely).", "default": true },
"parser_cache_min_size": { "type": "number", "description": "The minimum size a source string must be (in bytes) before it's considered eligible for caching.", "default": 1024 },
"parser_ext_renderers_enabled": { "type": "checkbox", "description": "Whether to enable external diagram renderer support, which is part of the parsedown parser. See the <code>parser_ext_renderers</code> setting below for more information.", "default": true },
"parser_ext_renderers": { "type": "parserext", "description": "Used by the parsedown parser as an object mapping fenced code block languages to their respective external renderers. Should be in the form <code>language_code</code> → <code>external renderer definition</code>. See the default for examples on how to define an external renderer.", "default": {
"parser_ext_renderers": { "type": "parserext", "description": "Used by the parsedown parser as an object mapping fenced code block languages to their respective external renderers. Should be in the form <code>language_code</code> → <code>external renderer definition</code>. See the default for examples on how to define an external renderer. Warning: On Windows, the enforcement of strict time limits is not possible. Beware of DoS attacks!", "default": {
"nomnoml": {
"name": "nomnoml",
"description": "The nomnoml UML diagram renderer. Requires the nomnoml-cli npm package to be globally installed.",
"description": "The nomnoml UML diagram renderer. Requires the nomnoml npm package to be globally installed.",
"url": "http://nomnoml.com/",
"cli": "nomnoml",
"cli_mode": "pipe",
"output_format": "image/png"
"cli": "nomnoml {input_file} {output_file} 0",
"cli_mode": "file",
"output_format": "image/svg+xml"
},
"plantuml": {
"name": "PlantUML",
@ -41,13 +41,22 @@
},
"lilypond": {
"name": "LilyPond",
"description": "A music notation typesetter. Requires lilypond to be installed.",
"description": "A music notation typesetter. Requires lilypond to be installed. WARNING: Don't enable lilypond if you allow anonymous edits. While every effort has been made to close loopholes, a DoS attack may still be possible against your server.",
"url": "http://lilypond.org/manuals.html",
"cli": "lilypondzzzzzz --silent -ddelete-intermediate-files -dbackend=eps -dresolution=600 --output {output_file} {input_file}",
"cli_mode": "file",
"output_format": "image/svg+xml"
},
"latexserver": {
"name": "Server-Side MathJax",
"description": "Client-side Mathjax via the 'enable_math_rendering' setting not your thing? Try it server-side instead! Requires the 'mathjax-node-cli' npm package to be globally installed.",
"url": "https://github.com/mathjax/mathjax-node-cli/blob/master/package.json#L41",
"cli": "tex2svg -- {input_text} >{output_file}",
"cli_mode": "substitution_pipe",
"output_format": "image/svg+xml"
}
} },
"parser_ext_time_limit": { "type": "number", "description": "The number of seconds external renderers are allowed to run for. Has no effect if external renderers are turned off. Also currently has no effect on Windows.", "default": 5 },
"interwiki_index_location": { "type": "text", "description": "The location to find the interwiki wiki definition file, which contains a list of wikis along with their names, prefixes, and root urls. May be a URL, or simply a file path - as it's passed to file_get_contents(). If left blank, interwiki link parsing is disabled.", "default": null },
"clean_raw_html": { "type": "checkbox", "description": "Whether page sources should be cleaned of HTML before rendering. It is STRONGLY recommended that you keep this option turned on.", "default": true },
"all_untrusted": { "type": "checkbox", "description": "Whether to treat both page sources and comment text as untrusted input. Untrusted input has additional restrictions to protect against XSS attacks etc. Turn on if your wiki allows anonymous edits.", "default": false},