# HG changeset patch # User Dan # Date 1243744689 14400 # Node ID f2ceea4fabe8e88a950347794b979496df0b335e # Parent cac93de16379573a0aa53e39bad9f07edaab1a05 Added support for replying and fixed a few security holes. TODO: search integration diff -r cac93de16379 -r f2ceea4fabe8 Gorilla.php --- a/Gorilla.php Sat May 30 02:52:13 2009 -0400 +++ b/Gorilla.php Sun May 31 00:38:09 2009 -0400 @@ -5,8 +5,9 @@ "Plugin URI" : "http://enanocms.org/plugin/gorilla", "Description" : "For The Toughest Pasting Jobs On Earth.™ The pastebin, Enano style. GeSHi plugin highly recommended.", "Author" : "Dan Fuhry", - "Version" : "0.1", - "Author URI" : "http://enanocms.org/" + "Version" : "0.1.1", + "Author URI" : "http://enanocms.org/", + "Version list" : ['0.1', '0.1.1'] } **!*/ @@ -79,6 +80,14 @@ $db->free_result(); $private = $flags & PASTE_PRIVATE ? true : false; $copy_from = $paste_id; + + if ( $flags & PASTE_PRIVATE ) + { + if ( @$_GET['hash'] !== gorilla_sign($paste_id, $text) ) + { + die_friendly($lang->get('etc_access_denied_short'), '

' . $lang->get('gorilla_msg_wrong_hash') . '

'); + } + } } $output->header(); @@ -107,13 +116,19 @@ var whitey = whiteOutElement(document.forms['gorilla_create']); + var parent = parseInt($('#gorilla_parent').val()); + if ( isNaN(parent) ) + parent = 0; + var json_packet = { highlight: $('#gorilla_highlight').val(), text: $('#gorilla_create_text').val(), is_private: $('#gorilla_private:checked').val() ? true : false, nick: $('#gorilla_nick').val(), title: $('#gorilla_title').val(), - ttl: parseInt($('.gorilla_ttl:checked').val()) + ttl: parseInt($('.gorilla_ttl:checked').val()), + parent: parent, + hash: $('#gorilla_hash').val(); }; json_packet = ajaxEscape(toJSONString(json_packet)); ajaxPost(makeUrlNS('Special', 'NewPaste/ajaxsubmit'), 'r=' + json_packet, function(ajax) @@ -248,8 +263,20 @@

+ +

+ get('gorilla_lbl_reply'); ?> +

+ + + + + + + + @@ -280,7 +307,9 @@ 'is_private' => 'boolean', 'nick' => 'string', 'title' => 'string', - 'ttl' => 'integer' + 'ttl' => 'integer', + 'parent' => 'integer', + 'hash' => 'string' ); $info = array(); @@ -318,10 +347,33 @@ 'is_private' => isset($_POST['is_private']), 'nick' => $_POST['nick'], 'title' => $_POST['title'], - 'ttl' => intval($_POST['ttl']) + 'ttl' => intval($_POST['ttl']), + 'parent' => intval($_POST['parent']), + 'hash' => $_POST['hash'] ); } + if ( $info['parent'] ) + { + // make sure we have the right hash + $q = $db->sql_query('SELECT paste_text FROM ' . table_prefix . "pastes WHERE paste_id = {$info['parent']};"); + if ( !$q ) + $db->_die(); + + if ( $db->numrows() > 0 ) + { + list($old_text) = $db->fetchrow_num(); + if ( $info['hash'] !== gorilla_sign($info['parent'], $old_text) ) + { + $info['parent'] = 0; + } + } + else + { + $info['parent'] = 0; + } + } + if ( !$have_permission ) { return '1;
' . $lang->get('etc_access_denied') . '
'; @@ -359,8 +411,8 @@ if ( $info['is_private'] ) $flags |= PASTE_PRIVATE; - $sql = 'INSERT INTO ' . table_prefix . "pastes( paste_title, paste_text, paste_author, paste_author_name, paste_author_ip, paste_language, paste_timestamp, paste_ttl, paste_flags ) VALUES\n" - . " ( {$info_db['title']}, {$info_db['text']}, $session->user_id, {$info_db['nick']}, '{$_SERVER['REMOTE_ADDR']}', {$info_db['highlight']}, $now, {$info_db['ttl']}, $flags );"; + $sql = 'INSERT INTO ' . table_prefix . "pastes( paste_title, paste_text, paste_author, paste_author_name, paste_author_ip, paste_language, paste_timestamp, paste_ttl, paste_flags, paste_parent ) VALUES\n" + . " ( {$info_db['title']}, {$info_db['text']}, $session->user_id, {$info_db['nick']}, '{$_SERVER['REMOTE_ADDR']}', {$info_db['highlight']}, $now, {$info_db['ttl']}, $flags, {$info_db['parent']} );"; if ( !$db->sql_query($sql) ) ( $is_ajax ) ? $db->die_json() : $db->_die(); @@ -374,7 +426,7 @@ $params = false; if ( $flags & PASTE_PRIVATE ) - $params = 'hash=' . hmac_sha1($paste_id, sha1($info['text'])); + $params = 'hash=' . gorilla_sign($paste_id, $info['text']); $paste_url = makeUrlComplete('Paste', $paste_id, $params, true); @@ -498,6 +550,24 @@ extract($data); $perms = $session->fetch_page_acl($paste_id, 'Paste'); + $localhash = false; + if ( $paste_flags & PASTE_PRIVATE ) + { + $localhash = gorilla_sign($paste_id, $paste_text); + } + + if ( $paste_flags & PASTE_PRIVATE || isset($_GET['delete']) ) + { + if ( @$_GET['hash'] !== $localhash ) + { + // allow viewing regardless if mod or admin + if ( !($session->user_level >= USER_LEVEL_MOD && !isset($_GET['delete'])) ) + { + die_friendly($lang->get('etc_access_denied_short'), '

' . $lang->get('gorilla_msg_wrong_hash') . '

'); + } + } + } + if ( isset($_GET['format']) ) { switch($_GET['format']) @@ -518,14 +588,6 @@ } } - if ( $paste_flags & PASTE_PRIVATE || isset($_GET['delete']) ) - { - if ( @$_GET['hash'] !== hmac_sha1($paste_id, sha1($paste_text)) ) - { - die_friendly($lang->get('etc_access_denied_short'), '

' . $lang->get('gorilla_msg_wrong_hash') . '

'); - } - } - $output->header(); $perm = $paste_author == $session->user_id ? 'delete_paste_own' : 'delete_paste_others'; @@ -541,7 +603,7 @@ } else { - $submit_url = makeUrlNS('Paste', $paste_id, 'delete&hash=' . hmac_sha1($paste_id, sha1($paste_text)), true); + $submit_url = makeUrlNS('Paste', $paste_id, 'delete&hash=' . gorilla_sign($paste_id, $paste_text), true); ?>

get('gorilla_msg_delete_confirm'); ?>

@@ -572,15 +634,40 @@ echo '
'; echo '
- ' . $lang->get('gorilla_msg_other_formats', array('plain_link' => makeUrlNS('Paste', $paste_id, 'format=text', true), 'download_link' => makeUrlNS('Paste', $paste_id, 'format=download', true))) . ' + ' . $lang->get('gorilla_msg_other_formats', array('plain_link' => makeUrlNS('Paste', $paste_id, 'format=text' . ( $localhash ? "&hash=$localhash" : '' ), true), 'download_link' => makeUrlNS('Paste', $paste_id, 'format=download' . ( $localhash ? "&hash=$localhash" : '' ), true))) . ' / ' . $lang->get('gorilla_btn_new_paste') . ' / - ' . $lang->get('gorilla_btn_copy_from_this') . ''; + ' . $lang->get('gorilla_btn_copy_from_this') . ''; + + if ( $paste_parent ) + { + // pull flags of parent + $q = $db->sql_query('SELECT paste_text, paste_flags FROM ' . table_prefix . "pastes WHERE paste_id = $paste_parent;"); + if ( !$q ) + $db->_die(); + + if ( $db->numrows() > 0 ) + { + list($parent_text, $parent_flags) = $db->fetchrow_num(); + $parenthash = false; + if ( $parent_flags & PASTE_PRIVATE ) + { + $parenthash = gorilla_sign($paste_parent, $parent_text); + } + + echo ' / ' . $lang->get('gorilla_msg_reply_to', array( + 'parent_link' => makeUrlNS('Paste', $paste_parent, ( $parenthash ? "hash=$parenthash" : '' ), true), + 'diff_link' => makeUrlNS('Paste', $paste_id, 'diff_parent' . ( $localhash ? "&hash=$localhash" : '' ), true), + 'parent_id' => $paste_parent + )); + } + $db->free_result($q); + } if ( $perms->get_permissions($perm) && $session->user_logged_in ) { - echo ' / ' . $lang->get('gorilla_btn_delete') . ''; + echo ' / ' . $lang->get('gorilla_btn_delete') . ''; } if ( $perms->get_permissions('mod_misc') ) { @@ -591,6 +678,15 @@ echo $pasteinfo; echo '
'; + if ( isset($_GET['diff_parent']) && isset($parent_text) ) + { + echo '

' . $lang->get('gorilla_btn_view_normal', array('orig_url' => makeUrlNS('Paste', $paste_id, ( $localhash ? "hash=$localhash" : '' ), true))) . '

'; + // convert to unix newlines to avoid confusing the diff engine (seen on Chromium on Linux) + echo RenderMan::diff(str_replace("\r\n", "\n", $parent_text), str_replace("\r\n", "\n", $paste_text)); + $output->footer(); + return; + } + if ( preg_match('/^## /m', $paste_text) ) { gorilla_show_text_multi($paste_text, $paste_language); @@ -635,7 +731,7 @@ function gorilla_show_text_multi($text, $lang) { $sections = preg_split('/^## .*$/m', $text); - $headingcount = preg_match_all('/^## (.+?)(?: \[([a-z_-]+)\])? *$/m', $text, $matches); + $headingcount = preg_match_all('/^## (.+?)(?: \[([a-z_-]+)\])? *\r?$/m', $text, $matches); // if we have one heading less than the number of sections, print the first section while ( count($sections) > $headingcount ) @@ -654,6 +750,11 @@ } } +function gorilla_sign($id, $text) +{ + return hmac_sha1($id, sha1($text)); +} + // make sure pastes are pruned on a regular basis function gorilla_prune_expired() { @@ -677,11 +778,16 @@ paste_timestamp int(12) NOT NULL DEFAULT 0, paste_ttl int(12) NOT NULL DEFAULT 86400, paste_flags int(8) NOT NULL DEFAULT 0, + paste_parent int(18) NOT NULL DEFAULT 0, PRIMARY KEY ( paste_id ) ) ENGINE=`MyISAM` CHARSET=`UTF8` COLLATE=`utf8_bin`; **!*/ +/**!upgrade from="0.1"; to="0.1.1"; dbms="mysql"; ** +ALTER TABLE {{TABLE_PREFIX}}pastes ADD COLUMN paste_parent int(18) NOT NULL DEFAULT 0; +**!*/ + /**!uninstall ** DROP TABLE {{TABLE_PREFIX}}pastes; **!*/ @@ -716,6 +822,7 @@ lbl_ttl_day: '1 day', lbl_ttl_month: '1 month', lbl_ttl_forever: 'forever', + lbl_reply: 'Reply to paste: #', msg_will_prompt_for_login: 'You are not logged in. You will be asked to log in when you click the submit button below.', btn_submit: 'Paste it!', @@ -736,6 +843,8 @@ btn_delete: 'rm', tip_delete: 'Delete this paste', tip_paste_ip: 'IP address of paste author', + msg_reply_to: 'parent: #%parent_id% (diff)', + btn_view_normal: 'Difference from parent (back to paste)', template_ns_string: 'paste', msg_paste_deleted: 'Paste deleted.',