Compare commits

...

3 Commits

10 changed files with 182 additions and 61 deletions

View File

@ -1,12 +1,14 @@
<?php
<?php declare(strict_types=1);
require_once "lib-htmgem.php";
require_once "lib-htmgem.inc.php";
require_once "lib-html.inc.php";
# The url argument is always absolute compared to the document root.
$url = @$_REQUEST["url"];
$urlRewriting = @$_REQUEST["rw"]=="1";
/* Installation page
/**
* Installation page
*
* Accessing directly /htmgem will make display the self-hosted documentation
* contained in "index.gmi". If it's removed, display an empty page with a
@ -17,13 +19,16 @@ if (empty($url)) {
http_response_code(403);
die("<!-- index.gmi missing -->");
}
$t = new \htmgem\GemTextTranslate_html(@file_get_contents("index.gmi"), true, "/htmgem");
echo $t->getFullHtml();
$gt_html = new \htmgem\GemTextTranslate_html(@file_get_contents("index.gmi"), true, "/htmgem");
echo \htmgem\html\getFullHtml($gt_html);
exit();
}
$documentRoot = $_SERVER['DOCUMENT_ROOT'];
/**
* Provides index.gmi if no page given
*/
if (!preg_match("/\.gmi$/", $url)) {
if ($url[-1] == "/")
$url = $url."index.gmi";
@ -49,20 +54,14 @@ switch(true) {
if ($go404) {
error_log("HtmGem: 404 $url $filePath");
http_response_code(404);
$page404 = <<<EOF
# ⚠ Page non trouvée
**$url**
=> .. 🔄 🔄
EOF;
$t = new \htmgem\GemTextTranslate_html($page404);
echo $t->getFullHtml();
$page404 = \htmgem\html\get404GmiPage("Page not found", $url);
$gt_html = new \htmgem\GemTextTranslate_html($page404);
echo \htmgem\html\getFullHtml($gt_html);
exit();
}
# to false only if textDecoration=0 in the URL
$textDecoration = "0" != @$_REQUEST['textDecoration'];
$gt_htmlextDecoration = "0" != @$_REQUEST['textDecoration'];
$fileContents = @file_get_contents($filePath);
# Removes the Byte Order Mark
@ -106,11 +105,11 @@ if ($urlRewriting)
$baseUrl = null;
else
$baseUrl = dirname($url);
$t = new \htmgem\GemTextTranslate_html($fileContents, $textDecoration, $baseUrl);
$gt_html = new \htmgem\GemTextTranslate_html($fileContents, $gt_htmlextDecoration, $baseUrl);
if ("none" == $style) {
$t->addCss("");
$gt_html->addCss("");
} elseif ("/" == @$style[0]) {
$t->addCss($style);
$gt_html->addCss($style);
} elseif (empty($style)) {
$parts = pathinfo($filePath);
$localCss = $parts["filename"].".css";
@ -118,12 +117,12 @@ if ("none" == $style) {
if (file_exists($localCssFilePath)) {
# Warning, using htmhem.php?url=… will make $localCss not found
# as the path is relative to htmgem.php and not / !
$t->addCss($localCss);
$gt_html->addCss($localCss);
}
} else { #TODO: regex check for $style
$t->addCss("/htmgem/css/$style.css");
$gt_html->addCss("/htmgem/css/$style.css");
}
echo $t->getFullHtml();
echo \htmgem\html\getFullHtml($gt_html);
?>

View File

@ -1,4 +1,4 @@
<?php
<?php declare(strict_types=1);
namespace htmgem;
@ -33,10 +33,10 @@ function gemtextParser($fileContents) {
yield array("mode" => "^^^");
} elseif ("#" == $line1) {
preg_match("/^(#{1,3})\s*(.+)?/", $line, $matches);
yield array("mode" => $matches[1], "title" => trim(@$matches[2]));
yield array("mode" => $matches[1], "title" => trim($matches[2]??""));
} elseif ("=>" == $line2) {
preg_match("/^=>\s*([^\s]+)(?:\s+(.*))?$/", $line, $matches);
yield array("mode" => "=>", "link" => trim(@$matches[1]), "text" => trim(@$matches[2]));
yield array("mode" => "=>", "link" => trim($matches[1]??""), "text" => trim($matches[2]??""));
} elseif ("```" == $line3) {
preg_match("/^```\s*(.*)$/", $line, $matches);
$current = array("mode" => "```", "alt" => trim($matches[1]), "texts" => array());
@ -197,6 +197,9 @@ class GemtextTranslate_html {
$this->cssList []= $css;
}
function getCss() { return $this->cssList; }
function getTitle() { return $this->pageTitle; }
const NARROW_NO_BREAK_SPACE = "&#8239;";
const DASHES
="" # U+2012 Figure Dash
@ -263,14 +266,20 @@ class GemtextTranslate_html {
$text = preg_replace("/ +/", " ", $text);
}
protected static function resolve_path($path) {
/**
* Resolve $path interpretating / . and ..
* @param $path str
* @returns "/" if .. goes above the limit
*/
public static function resolve_path($path) {
if (empty($path)) return "";
$absolute = "/"==$path[0];
$parts = array_filter(explode("/", $path), 'strlen');
$chuncks = array();
foreach ($parts as $part) {
if ('.' == $part) continue;
if ('..' == $part) {
array_pop($chuncks);
if (is_null(array_pop($chuncks))) return "/";
} else {
$chuncks[] = $part;
}
@ -374,34 +383,6 @@ class GemtextTranslate_html {
$this->translatedGemtext = $output;
}
function getFullHtml() {
if (!$this->cssList)
$css = array("/htmgem/css/htmgem.css");
else
$css = $this->cssList;
$output = <<<EOL
<!DOCTYPE html>
<html>
<head>
<title>{$this->pageTitle}</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
EOL;
foreach ($css as $c) {
$output .= "\n<link type='text/css' rel='StyleSheet' href='$c'>\n";
}
$output .= <<<EOL
</head>
<body>\n
EOL;
$output .= $this->translatedGemtext;
$output .= "</body>\n</html>\n";
echo $output;
}
public function __toString() {
return $this->translatedGemtext;
}
} // GemTextTranslate_html
?>

44
lib-html.inc.php Normal file
View File

@ -0,0 +1,44 @@
<?php declare(strict_types=1);
namespace htmgem\html;
mb_internal_encoding("UTF-8");
mb_regex_encoding("UTF-8");
/**
* Returns a full HTML page base
*/
function getFullHtml(\htmgem\GemtextTranslate_html $gt_html) {
$css = $gt_html->getCss();
if (!$css) $css = array("/htmgem/css/htmgem.css");
$output = <<<EOL
<!DOCTYPE html>
<html lang="">
<head>
<title>{$gt_html->getTitle()}</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
EOL;
foreach ($css as $c) {
$output .= "\n<link type='text/css' rel='StyleSheet' href='$c'>\n";
}
$output .= <<<EOL
</head>
<body>\n
EOL;
$output .= $gt_html->translatedGemtext;
$output .= "</body>\n</html>\n";
return $output;
}
function get404Gmipage($message, $url) {
return <<<EOF
# ⚠ $message
**$url**
=> .. 🔄 🔄
EOF;
}
?>

View File

@ -2,7 +2,7 @@
$fileName = $argv[1];
require_once dirname(__FILE__)."/../lib-htmgem.php";
require_once dirname(__FILE__)."/../lib-htmgem.inc.php";
$text = file_get_contents($fileName);
$parsedGemtext = \htmgem\gemtextParser($text);

View File

@ -2,7 +2,7 @@
$fileName = $argv[1];
require_once dirname(__FILE__)."/../../lib-htmgem.php";
require_once dirname(__FILE__)."/../../lib-htmgem.inc.php";
$text = file_get_contents($fileName);
$gt_gemtext = new \htmgem\GemtextTranslate_gemtext($text);

View File

@ -2,7 +2,7 @@
$fileName = $argv[1];
require_once dirname(__FILE__)."/../../lib-htmgem.php";
require_once dirname(__FILE__)."/../../lib-htmgem.inc.php";
$text = file_get_contents($fileName);
$gt_gemtext = new \htmgem\GemtextTranslate_html($text);

96
tests/miscTest.php Normal file
View File

@ -0,0 +1,96 @@
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
require_once dirname(__FILE__)."/../lib-htmgem.inc.php";
final class miscTest extends TestCase {
public function test_resolveLink(): void {
$this->assertSame(
\htmgem\GemtextTranslate_html::resolve_path(""),
"",
"empty link"
);
$this->assertSame(
\htmgem\GemtextTranslate_html::resolve_path("test"),
"test",
"single word"
);
$this->assertSame(
\htmgem\GemtextTranslate_html::resolve_path(" "),
" ",
"single space"
);
$this->assertSame(
\htmgem\GemtextTranslate_html::resolve_path(" A B "),
" A B ",
"several space"
);
$this->assertSame(
\htmgem\GemtextTranslate_html::resolve_path("/"),
"/",
"one slash"
);
$this->assertSame(
\htmgem\GemtextTranslate_html::resolve_path("//"),
"/",
"two slashes"
);
$this->assertSame(
\htmgem\GemtextTranslate_html::resolve_path("/////"),
"/",
"five slashes"
);
$this->assertSame(
\htmgem\GemtextTranslate_html::resolve_path("one/"),
"one",
"strip the last slash"
);
$this->assertSame(
\htmgem\GemtextTranslate_html::resolve_path("/two"),
"/two",
"slash at the beginning"
);
$this->assertSame(
\htmgem\GemtextTranslate_html::resolve_path("/two/"),
"/two",
"slash at the beginning and the end"
);
$this->assertSame(
\htmgem\GemtextTranslate_html::resolve_path("one/two/"),
"one/two",
"only the last slash remains"
);
$this->assertSame(
\htmgem\GemtextTranslate_html::resolve_path("one/two/three//"),
"one/two/three",
"strip the last slashes"
);
$this->assertSame(
\htmgem\GemtextTranslate_html::resolve_path("one/../"),
"",
"empty one"
);
$this->assertSame(
\htmgem\GemtextTranslate_html::resolve_path("one/two/../"),
"one",
"empty one two"
);
$this->assertSame(
\htmgem\GemtextTranslate_html::resolve_path("one/two/../.."),
"",
"empty one two twice"
);
$this->assertSame(
\htmgem\GemtextTranslate_html::resolve_path("one/../two/./../three"),
"three",
"waltz"
);
$this->assertSame(
\htmgem\GemtextTranslate_html::resolve_path("one/../.."),
"/",
"directory traversal"
);
}
}

View File

@ -1,7 +1,7 @@
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
require_once dirname(__FILE__)."/../lib-htmgem.php";
require_once dirname(__FILE__)."/../lib-htmgem.inc.php";
function parse($text): array {
return iterator_to_array(\htmgem\gemtextParser($text));

View File

@ -2,7 +2,7 @@
use PHPUnit\Framework\TestCase;
$dirname_file = dirname(__FILE__);
require_once "$dirname_file/../lib-htmgem.php";
require_once "$dirname_file/../lib-htmgem.inc.php";
require_once "$dirname_file/utils.inc.php";
function translate($text): string {

View File

@ -2,11 +2,12 @@
use PHPUnit\Framework\TestCase;
$dirname_file = dirname(__FILE__);
require_once "$dirname_file/../lib-htmgem.php";
require_once "$dirname_file/../lib-htmgem.inc.php";
require_once "$dirname_file/utils.inc.php";
function translateHtml($text): string {
return strval(new htmgem\GemtextTranslate_html($text));
$gt_html = new htmgem\GemtextTranslate_html($text);
return strval($gt_html->translatedGemtext);
}
final class translateToHtmlTest extends TestCase {