diff --git a/core/event.php b/core/event.php index b035269c..0d0f2caf 100644 --- a/core/event.php +++ b/core/event.php @@ -72,16 +72,6 @@ class PageRequestEvent extends Event // break the path into parts $args = explode('/', $path); - // voodoo so that an arg can contain a slash; is - // this still needed? - if (strpos($path, "^") !== false) { - $unescaped = []; - foreach ($args as $part) { - $unescaped[] = _decaret($part); - } - $args = $unescaped; - } - $this->args = $args; $this->arg_count = count($args); } @@ -152,7 +142,7 @@ class PageRequestEvent extends Event { $search_terms = []; if ($this->count_args() === 2) { - $search_terms = Tag::explode($this->get_arg(0)); + $search_terms = Tag::explode(Tag::decaret($this->get_arg(0))); } return $search_terms; } diff --git a/core/imageboard/tag.php b/core/imageboard/tag.php index f43bdcfb..079b06ac 100644 --- a/core/imageboard/tag.php +++ b/core/imageboard/tag.php @@ -152,7 +152,6 @@ class Tag return $tag_array; } - public static function sqlify(string $term): string { global $database; @@ -165,4 +164,51 @@ class Tag // $term = str_replace("?", "_", $term); return $term; } + + /** + * Kind of like urlencode, but using a custom scheme so that + * tags always fit neatly between slashes in a URL. Use this + * when you want to put an arbitrary tag into a URL. + */ + public static function caret(string $input): string + { + $to_caret = [ + "^" => "^", + "/" => "s", + "\\" => "b", + "?" => "q", + "&" => "a", + ]; + + foreach($to_caret as $from => $to) { + $input = str_replace($from, '^' . $to, $input); + } + return $input; + } + + /** + * Use this when you want to get a tag out of a URL + */ + public static function decaret(string $str): string + { + $from_caret = [ + "^" => "^", + "s" => "/", + "b" => "\\", + "q" => "?", + "a" => "&", + ]; + + $out = ""; + $length = strlen($str); + for ($i=0; $i<$length; $i++) { + if ($str[$i] == "^") { + $i++; + $out .= $from_caret[$str[$i]] ?? ''; + } else { + $out .= $str[$i]; + } + } + return $out; + } } diff --git a/core/polyfills.php b/core/polyfills.php index 2c0ca5bb..a0ed4597 100644 --- a/core/polyfills.php +++ b/core/polyfills.php @@ -449,32 +449,9 @@ function int_escape(?string $input): int */ function url_escape(?string $input): string { - /* - Shish: I have a feeling that these three lines are important, possibly for searching for tags with slashes in them like fate/stay_night - green-ponies: indeed~ - - $input = str_replace('^', '^^', $input); - $input = str_replace('/', '^s', $input); - $input = str_replace('\\', '^b', $input); - - /* The function idn_to_ascii is used to support Unicode domains / URLs as well. - See here for more: http://php.net/manual/en/function.filter-var.php - However, it is only supported by PHP version 5.3 and up - - if (function_exists('idn_to_ascii')) { - return filter_var(idn_to_ascii($input), FILTER_SANITIZE_URL); - } else { - return filter_var($input, FILTER_SANITIZE_URL); - } - */ if (is_null($input)) { return ""; } - $input = str_replace('^', '^^', $input); - $input = str_replace('/', '^s', $input); - $input = str_replace('\\', '^b', $input); - $input = str_replace('?', '^q', $input); - $input = str_replace('&', '^a', $input); $input = rawurlencode($input); return $input; } diff --git a/core/tests/polyfills.test.php b/core/tests/polyfills.test.php index 6fa3389a..2a833cbc 100644 --- a/core/tests/polyfills.test.php +++ b/core/tests/polyfills.test.php @@ -30,7 +30,7 @@ class PolyfillsTest extends \PHPUnit\Framework\TestCase public function test_url_escape() { - $this->assertEquals(url_escape("^\o/^"), "%5E%5E%5Ebo%5Es%5E%5E"); + $this->assertEquals(url_escape("^\o/^"), "%5E%5Co%2F%5E"); $this->assertEquals(url_escape(null), ""); } diff --git a/core/tests/tag.test.php b/core/tests/tag.test.php new file mode 100644 index 00000000..2afd2752 --- /dev/null +++ b/core/tests/tag.test.php @@ -0,0 +1,19 @@ +assertEquals("foo", Tag::decaret("foo")); + $this->assertEquals("foo?", Tag::decaret("foo^q")); + $this->assertEquals("a^b/c\\d?e&f", Tag::decaret("a^^b^sc^bd^qe^af")); + } + + public function test_decaret() + { + $this->assertEquals("foo", Tag::caret("foo")); + $this->assertEquals("foo^q", Tag::caret("foo?")); + $this->assertEquals("a^^b^sc^bd^qe^af", Tag::caret("a^b/c\\d?e&f")); + } +} diff --git a/core/util.php b/core/util.php index 26e00fae..3837216b 100644 --- a/core/util.php +++ b/core/util.php @@ -606,41 +606,6 @@ function _fatal_error(Exception $e): void } } -/** - * Turn ^^ into ^ and ^s into / - * - * Necessary because various servers and various clients - * think that / is special... - */ -function _decaret(string $str): string -{ - $out = ""; - $length = strlen($str); - for ($i=0; $i<$length; $i++) { - if ($str[$i] == "^") { - $i++; - if ($str[$i] == "^") { - $out .= "^"; - } - if ($str[$i] == "s") { - $out .= "/"; - } - if ($str[$i] == "b") { - $out .= "\\"; - } - if ($str[$i] == "q") { - $out .= "?"; - } - if ($str[$i] == "a") { - $out .= "&"; - } - } else { - $out .= $str[$i]; - } - } - return $out; -} - function _get_user(): User { global $config, $page; diff --git a/ext/index/main.php b/ext/index/main.php index f59ea7f4..0652646f 100644 --- a/ext/index/main.php +++ b/ext/index/main.php @@ -25,7 +25,7 @@ class Index extends Extension if ($event->page_matches("post/list")) { if (isset($_GET['search'])) { // implode(explode()) to resolve aliases and sanitise - $search = url_escape(Tag::implode(Tag::explode($_GET['search'], false))); + $search = url_escape(Tag::caret(Tag::implode(Tag::explode($_GET['search'], false)))); if (empty($search)) { $page->set_mode(PageMode::REDIRECT); $page->set_redirect(make_link("post/list/1")); diff --git a/ext/rss_images/main.php b/ext/rss_images/main.php index 992f8f39..3a5638a4 100644 --- a/ext/rss_images/main.php +++ b/ext/rss_images/main.php @@ -9,7 +9,7 @@ class RSSImages extends Extension $title = $config->get_string(SetupConfig::TITLE); if (count($event->search_terms) > 0) { - $search = html_escape(implode(' ', $event->search_terms)); + $search = url_escape(Tag::caret(Tag::implode($event->search_terms))); $page->add_html_header(""); } else {