diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..e187950 --- /dev/null +++ b/composer.json @@ -0,0 +1,19 @@ +{ + "name": "aftercase/ponepaste", + "description": "PonePaste can store green", + "minimum-stability": "stable", + "license": "proprietary", + "authors": [ + { + "name": "aftercase" + }, + { + "name": "appledash" + } + ], + "require": { + "scrivo/highlight.php": "v9.18.1.7", + "ext-pdo": "*", + "ext-openssl": "*" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..0aa237f --- /dev/null +++ b/composer.lock @@ -0,0 +1,95 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "6150c75f4650b6bf4b3f2cb2cbce0bda", + "packages": [ + { + "name": "scrivo/highlight.php", + "version": "v9.18.1.7", + "source": { + "type": "git", + "url": "https://github.com/scrivo/highlight.php.git", + "reference": "05996fcc61e97978d76ca7d1ac14b65e7cd26f91" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/scrivo/highlight.php/zipball/05996fcc61e97978d76ca7d1ac14b65e7cd26f91", + "reference": "05996fcc61e97978d76ca7d1ac14b65e7cd26f91", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "php": ">=5.4" + }, + "require-dev": { + "phpunit/phpunit": "^4.8|^5.7", + "sabberworm/php-css-parser": "^8.3", + "symfony/finder": "^2.8|^3.4", + "symfony/var-dumper": "^2.8|^3.4" + }, + "type": "library", + "autoload": { + "psr-0": { + "Highlight\\": "", + "HighlightUtilities\\": "" + }, + "files": [ + "HighlightUtilities/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Geert Bergman", + "homepage": "http://www.scrivo.org/", + "role": "Project Author" + }, + { + "name": "Vladimir Jimenez", + "homepage": "https://allejo.io", + "role": "Maintainer" + }, + { + "name": "Martin Folkers", + "homepage": "https://twobrain.io", + "role": "Contributor" + } + ], + "description": "Server side syntax highlighter that supports 185 languages. It's a PHP port of highlight.js", + "keywords": [ + "code", + "highlight", + "highlight.js", + "highlight.php", + "syntax" + ], + "support": { + "issues": "https://github.com/scrivo/highlight.php/issues", + "source": "https://github.com/scrivo/highlight.php" + }, + "funding": [ + { + "url": "https://github.com/allejo", + "type": "github" + } + ], + "time": "2021-07-09T00:30:39+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.1.0" +} diff --git a/config/green.lang.json b/config/green.lang.json new file mode 100644 index 0000000..ea878ba --- /dev/null +++ b/config/green.lang.json @@ -0,0 +1,10 @@ +{ + "aliases": ["greentext"], + "contains": [ + { + "className": "comment", + "begin": ">", + "end": "$" + } + ] +} \ No newline at end of file diff --git a/includes/common.php b/includes/common.php index 53db32d..e5dbb28 100644 --- a/includes/common.php +++ b/includes/common.php @@ -2,7 +2,7 @@ if (!defined('IN_PONEPASTE')) { die('This file may not be accessed directly.'); } - +require_once(__DIR__ . '/../vendor/autoload.php'); require_once(__DIR__ . '/config.php'); require_once(__DIR__ . '/functions.php'); require_once(__DIR__ . '/DatabaseHandle.class.php'); @@ -53,7 +53,7 @@ function getSiteTotal_unique_views(DatabaseHandle $conn) : int { * @return string HTML-escaped string */ function pp_html_escape(string $unescaped) : string { - return htmlentities($unescaped, ENT_QUOTES, 'UTF-8', false); + return htmlspecialchars($unescaped, ENT_QUOTES, 'UTF-8', false); } function updatePageViews(DatabaseHandle $conn) : void { diff --git a/paste.php b/paste.php index 8d265b4..149b8d2 100644 --- a/paste.php +++ b/paste.php @@ -24,10 +24,13 @@ require_once('includes/functions.php'); require_once('includes/Tag.class.php'); require_once('includes/passwords.php'); + require_once('includes/Parsedown/Parsedown.php'); require_once('includes/Parsedown/ParsedownExtra.php'); require_once('includes/Parsedown/SecureParsedown.php'); +use Highlight\Highlighter; + function rawView($content, $p_code) { if ($p_code) { header('Content-Type: text/plain'); @@ -41,10 +44,11 @@ $paste_id = intval(trim($_REQUEST['id'])); updatePageViews($conn); +// This is used in the theme files. +$totalpastes = getSiteTotalPastes($conn); + // Get paste favorite count -$query = $conn->prepare('SELECT COUNT(*) FROM pins WHERE paste_id = ?'); -$query->execute([$paste_id]); -$fav_count = intval($query->fetch(PDO::FETCH_NUM)[0]); +$fav_count = $conn->querySelectOne('SELECT COUNT(*) FROM pins WHERE paste_id = ?', [$paste_id], PDO::FETCH_NUM)[0]; // Get paste info $row = $conn->querySelectOne( @@ -54,9 +58,6 @@ $row = $conn->querySelectOne( WHERE pastes.id = ?', [$paste_id]); -// This is used in the theme files. -$totalpastes = getSiteTotalPastes($conn); - $notfound = null; $is_private = false; @@ -64,156 +65,150 @@ if ($row === null) { header('HTTP/1.1 404 Not Found'); $notfound = $lang['notfound']; // "Not found"; goto Not_Valid_Paste; -} else { - $paste_owner_id = (int) $row['user_id']; - $paste_title = $row['title']; - $paste_code = $row['code']; +} - $paste = [ - 'title' => $paste_title, - 'created_at' => (new DateTime($row['created_at']))->format('jS F Y h:i:s A'), - 'updated_at' => (new DateTime($row['updated_at']))->format('jS F Y h:i:s A'), - 'user_id' => $paste_owner_id, - 'member' => $row['member'], - 'views' => $row['views'], - 'code' => $paste_code, - 'tags' => getPasteTags($conn, $paste_id) - ]; +$paste_owner_id = (int) $row['user_id']; +$paste_title = $row['title']; +$paste_code = $row['code']; +$using_highlighter = $paste_code !== 'pastedown'; - $p_content = $row['content']; - $p_visible = $row['visible']; - $p_expiry = Trim($row['expiry']); - $p_password = $row['password']; - $p_encrypt = (bool) $row['encrypt']; +$paste = [ + 'title' => $paste_title, + 'created_at' => (new DateTime($row['created_at']))->format('jS F Y h:i:s A'), + 'updated_at' => (new DateTime($row['updated_at']))->format('jS F Y h:i:s A'), + 'user_id' => $paste_owner_id, + 'member' => $row['member'], + 'views' => $row['views'], + 'code' => $paste_code, + 'tags' => getPasteTags($conn, $paste_id) +]; + +$p_content = $row['content']; +$p_visible = $row['visible']; +$p_expiry = $row['expiry']; +$p_password = $row['password']; +$p_encrypt = (bool) $row['encrypt']; - $is_private = $row['visible'] === '2'; - $private_error = false; +$is_private = $row['visible'] === '2'; - if ($is_private && (!$current_user || $current_user->user_id !== $paste_owner_id)) { - $notfound = $lang['privatepaste']; //" This is a private paste. If you created this paste, please login to view it."; - $private_error = true; +if ($is_private && (!$current_user || $current_user->user_id !== $paste_owner_id)) { + $notfound = $lang['privatepaste']; //" This is a private paste. If you created this paste, please login to view it."; + goto Not_Valid_Paste; +} + +/* Verify paste password */ +$password_required = $p_password !== null && $p_password !== 'NONE'; +$password_valid = true; +$password_candidate = ''; + +if ($password_required) { + if (!empty($_POST['mypass'])) { + $password_candidate = $_POST['mypass']; + } else if (!empty($_GET['password'])) { + $password_candidate = @base64_decode($_GET['password']); + } + + if (empty($password_candidate)) { + $password_valid = false; + $error = $lang['pwdprotected']; // 'Password protected paste'; + goto Not_Valid_Paste; + } elseif (!pp_password_verify($password_candidate, $p_password)) { + $password_valid = false; + $error = $lang['wrongpassword']; // 'Wrong password'; goto Not_Valid_Paste; - } - - if (!empty($p_expiry) && $p_expiry !== 'SELF') { - $input_time = $p_expiry; - $current_time = mktime(date("H"), date("i"), date("s"), date("n"), date("j"), date("Y")); - if ($input_time < $current_time) { - $notfound = $lang['expired']; - $p_private_error = 1; - goto Not_Valid_Paste; - } - } - - if ($p_encrypt == 1) { - $p_content = openssl_decrypt($p_content, PP_ENCRYPTION_ALGO, PP_ENCRYPTION_KEY); - } - - $op_content = trim(htmlspecialchars_decode($p_content)); - - // Download the paste - if (isset($_GET['download'])) { - if ($p_password == "NONE" || $p_password === null) { - doDownload($paste_id, $paste_title, $p_member, $op_content, $paste_code); - exit(); - } else { - if (isset($_GET['password'])) { - if (pp_password_verify($_GET['password'], $p_password)) { - doDownload($paste_id, $paste_title, $p_member, $op_content, $paste_code); - exit(); - } else { - $error = $lang['wrongpassword']; // 'Wrong password'; - } - } else { - $error = $lang['pwdprotected']; // 'Password protected paste'; - } - } - } - - // Raw view - if (isset($_GET['raw'])) { - if ($p_password == "NONE" || $p_password === null) { - rawView($op_content, $paste_code); - exit(); - } else { - if (isset($_GET['password'])) { - if (pp_password_verify($_GET['password'], $p_password)) { - rawView($op_content, $paste_code); - exit(); - } else { - $error = $lang['wrongpassword']; // 'Wrong password'; - } - } else { - $error = $lang['pwdprotected']; // 'Password protected paste'; - } - } - } - - // Preprocess - $highlight = array(); - $prefix_size = strlen('!highlight!'); - $lines = explode("\n", $p_content); - $p_content = ""; - foreach ($lines as $idx => $line) { - if (substr($line, 0, $prefix_size) == '!highlight!') { - $highlight[] = $idx + 1; - $line = substr($line, $prefix_size); - } - $p_content .= $line . "\n"; - } - - $p_content = rtrim($p_content); - - // Apply syntax highlight - $p_content = htmlspecialchars_decode($p_content); - if ($paste_code === "pastedown") { - $Parsedown = new Parsedown(); - $Parsedown->setSafeMode(true); - $p_content = $Parsedown->text($p_content); - } else { - $geshi = new GeSHi($p_content, $paste_code, 'includes/geshi/'); - - $geshi->enable_classes(); - $geshi->set_header_type(GESHI_HEADER_DIV); - $geshi->set_line_style('color: #aaaaaa; width:auto;'); - $geshi->set_code_style('color: #757584;'); - if (count($highlight)) { - $geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS); - $geshi->highlight_lines_extra($highlight); - $geshi->set_highlight_lines_extra_style('color:#399bff;background:rgba(38,92,255,0.14);'); - } else { - $geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS, 2); - } - $p_content = $geshi->parse_code(); - $style = $geshi->get_stylesheet(); - $ges_style = ''; - } - - // Embed view after GeSHI is applied so that $p_code is syntax highlighted as it should be. - if (isset($_GET['embed'])) { - if ($p_password == "NONE" || $p_password === null) { - embedView($paste_id, $paste_title, $p_content, $paste_code, $title, $baseurl, $ges_style, $lang); - exit(); - } else { - if (isset($_GET['password'])) { - if (pp_password_verify($_GET['password'], $p_password)) { - embedView($paste_id, $paste_title, $p_content, $paste_code, $title, $p_baseurl, $ges_style, $lang); - exit(); - } else { - $error = $lang['wrongpassword']; // 'Wrong password'; - } - } else { - $error = $lang['pwdprotected']; // 'Password protected paste'; - } - } } } -require_once('theme/' . $default_theme . '/header.php'); -if ($p_password == "NONE" || $p_password === null) { - // No password & diplay the paste +if (!empty($p_expiry) && $p_expiry !== 'SELF') { + $input_time = $p_expiry; + $current_time = mktime(date("H"), date("i"), date("s"), date("n"), date("j"), date("Y")); + if ($input_time < $current_time) { + $notfound = $lang['expired']; + goto Not_Valid_Paste; + } +} +if ($p_encrypt == 1) { + $p_content = openssl_decrypt($p_content, PP_ENCRYPTION_ALGO, PP_ENCRYPTION_KEY); +} + +$op_content = trim(htmlspecialchars_decode($p_content)); + +// Download the paste +if (isset($_GET['download'])) { + doDownload($paste_id, $paste_title, $p_member, $op_content, $paste_code); + exit(); +} + +// Raw view +if (isset($_GET['raw'])) { + rawView($op_content, $paste_code); + exit(); +} + +// Preprocess +$highlight = array(); +$prefix_size = strlen('!highlight!'); +$lines = explode("\n", $p_content); +$p_content = ""; +foreach ($lines as $idx => $line) { + if (substr($line, 0, $prefix_size) == '!highlight!') { + $highlight[] = $idx + 1; + $line = substr($line, $prefix_size); + } + $p_content .= $line . "\n"; +} + +$p_content = rtrim($p_content); + +// Apply syntax highlight +$p_content = htmlspecialchars_decode($p_content); +if ($paste_code === "pastedown") { + $parsedown = new Parsedown(); + $parsedown->setSafeMode(true); + $p_content = $parsedown->text($p_content); +} else { + Highlighter::registerLanguage('green', 'config/green.lang.json'); + $hl = new Highlighter(); + $highlighted = $hl->highlight($paste_code == 'text' ? 'plaintext' : $paste_code, $p_content)->value; + $lines = HighlightUtilities\splitCodeIntoArray($highlighted); + //$highlight = new Highlighter(); + //$p_content = $highlight->highlight($paste_code, $p_content)->value; + + //$p_content = linkify($p_content); + + $geshi = new GeSHi($p_content, $paste_code, 'includes/geshi/'); + + $geshi->enable_classes(); + $geshi->set_header_type(GESHI_HEADER_DIV); + $geshi->set_line_style('color: #aaaaaa; width:auto;'); + $geshi->set_code_style('color: #757584;'); + if (count($highlight)) { + $geshi->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS); + $geshi->highlight_lines_extra($highlight); + $geshi->set_highlight_lines_extra_style('color:#399bff;background:rgba(38,92,255,0.14);'); + } else { + $geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS, 2); + } + $p_content = $geshi->parse_code(); + $style = $geshi->get_stylesheet(); + $ges_style = ''; +} + +// Embed view after highlighting is applied so that $p_code is syntax highlighted as it should be. +if (isset($_GET['embed'])) { + embedView($paste_id, $paste_title, $p_content, $paste_code, $title, $baseurl, $ges_style, $lang); + exit(); +} + +require_once('theme/' . $default_theme . '/header.php'); +if ($password_required && $password_valid) { + /* base64 here means that the password is exposed in the URL, technically - how to handle this better? */ + $p_download = "paste.php?download&id=$paste_id&password=" . base64_encode($password_candidate); + $p_raw = "paste.php?raw&id=$paste_id&password=" . base64_encode($password_candidate); + $p_embed = "paste.php?embed&id=$paste_id&password=" . base64_encode($password_candidate); +} else { // Set download URL if (PP_MOD_REWRITE) { $p_download = "download/$paste_id"; @@ -224,49 +219,24 @@ if ($p_password == "NONE" || $p_password === null) { $p_raw = "paste.php?raw&id=$paste_id"; $p_embed = "paste.php?embed&id=$paste_id"; } - - - // View counter - if (@$_SESSION['not_unique'] !== $paste_id) { - $_SESSION['not_unique'] = $paste_id; - $conn->query("UPDATE pastes SET views = (views + 1) where id = ?", [$paste_id]); - } - - // Theme - require_once('theme/' . $default_theme . '/view.php'); - if ($p_expiry == "SELF") { - $conn->query('DELETE FROM pastes WHERE id = ?', [$paste_id]); - } -} else { - $p_download = "paste.php?download&id=$paste_id&password=" . pp_password_hash(isset($_POST['mypass'])); - $p_raw = "paste.php?raw&id=$paste_id&password=" . pp_password_hash(isset($_POST['mypass'])); - // Check password - if (isset($_POST['mypass'])) { - if (pp_password_verify($_POST['mypass'], $p_password)) { - // Theme - require_once('theme/' . $default_theme . '/view.php'); - if ($p_expiry == "SELF") { - $conn->prepare('DELETE FROM pastes WHERE id = ?') - ->execute([$paste_id]); - } - } else { - $error = $lang['wrongpwd']; //"Password is wrong"; - require_once('theme/' . $default_theme . '/errors.php'); - } - } else { - // Display errors - require_once('theme/' . $default_theme . '/errors.php'); - } } +// View counter +if (@$_SESSION['not_unique'] !== $paste_id) { + $_SESSION['not_unique'] = $paste_id; + $conn->query("UPDATE pastes SET views = (views + 1) where id = ?", [$paste_id]); +} + +require_once('theme/' . $default_theme . '/view.php'); + Not_Valid_Paste: -// Private paste not valid -if ($is_private || $notfound) { + +if ($is_private || $notfound || !$password_valid) { // Display errors require_once('theme/' . $default_theme . '/header.php'); require_once('theme/' . $default_theme . '/errors.php'); } // Footer -require_once('theme/' . $default_theme . '/footer.php'); + require_once('theme/' . $default_theme . '/footer.php'); diff --git a/theme/bulma/view.php b/theme/bulma/view.php index 8faeaff..bba41d4 100644 --- a/theme/bulma/view.php +++ b/theme/bulma/view.php @@ -74,6 +74,9 @@ function setupTagsInput() { })(jQuery); }); + + + - Bulma theme @@ -98,8 +101,6 @@ $selectedloader = "$bg[$i]"; // set variable equal to which random filename was ?>