/** * returns a filter object to use for this * * @param string $name * @return InputFilter */ public final function filter($name) { if ($this->_input_filter !== null) { return $this->_input_filter->filter($name); } App::getInstance()->includeFile('Sonic/InputFilter.php'); $this->_input_filter = new InputFilter($this->request()); return $this->_input_filter->filter($name); }
function test_complete_filtering_run() { $this->assert_equal(InputFilter::filter('<p>I am <div><script src=\\"ohnoes\\" /><a>not a paragraph.</a><p CLASS=old><span> Or am I?</span>'), '<p>I am <div><a>not a paragraph.</a><p><span> Or am I?</span>'); $this->assert_equal(InputFilter::filter('<p onClick=\\"window.alert(\'stole yer cookies!\');\\">Do not click here.</p>\\n<script>alert(\\"See this?\\")</script>'), '<p>Do not click here.</p>\\n'); // http://ha.ckers.org/blog/20070124/stopping-xss-but-allowing-html-is-hard/ $this->assert_equal(InputFilter::filter('<IMG src=\\"http://ha.ckers.org/\\" style\\"=\\"style=\\"a/onerror=alert(String.fromCharCode(88,83,83))//\\" &gt;`>'), 'onerror=alert(String.fromCharCode(88,83,83))//\\" &`>'); $this->assert_equal(InputFilter::filter('<b>Hello world</b>\\n\\nThis is a <test>test</test> post.\\n\\nHere\'s a first XSS attack. <<SCRIPT>alert(\'XSS\');//<</SCRIPT>\\n\\nHere\'s a second try at a <a href=\\"#\\">second link</a>.\\n\\nHere\'s a second XSS attack. <IMG SRC=\\"  javascript:alert(\'XSS\');\\">\\n\\nHere\'s a third link hopefully <a href=\\"#\\">it won\'t get removed</a>.\\n\\n<em>Thanks!</em>'), '<b>Hello world</b>\\n\\nThis is a post.\\n\\nHere\'s a first XSS attack. '); $this->assert_equal(InputFilter::filter('<<test>script>alert(\'boom\');</test>'), ''); $this->assert_equal(InputFilter::filter('<<test></test>script>alert(\'boom\');'), ''); $this->assert_equal(InputFilter::filter('<<test><</test>script>alert(\'boom\');'), ''); $this->assert_equal(InputFilter::filter('<ScRIpT>alert(\'whee\');</SCRiPT>'), ''); }
protected static function fetch_backtype($url) { $backtype = array(); $cacheName = "backtype-{$url}"; if (Cache::has($cacheName)) { foreach (Cache::get($cacheName) as $cachedBacktype) { $cachedBacktype->date = HabariDateTime::date_create($cachedBacktype->date); $backtype[] = $cachedBacktype; } return $backtype; } $connectData = json_decode(file_get_contents("http://api.backtype.com/comments/connect.json?url={$url}&key=key&itemsperpage=10000")); if (isset($connectData->comments)) { foreach ($connectData->comments as $dat) { $comment = new StdClass(); switch ($dat->entry_type) { case 'tweet': $comment->id = 'backtype-twitter-' . $dat->tweet_id; $comment->url = 'http://twitter.com/' . $dat->tweet_from_user . '/status/' . $dat->tweet_id; $comment->name = '@' . $dat->tweet_from_user . ' (via Backtype: Twitter)'; $comment->content_out = InputFilter::filter($dat->tweet_text); $comment->date = $dat->tweet_created_at; break; case 'comment': $comment->id = 'backtype-comment-' . $dat->comment->id; $comment->url = $dat->comment->url; $comment->name = $dat->author->name . ' (via Backtype: ' . InputFilter::filter($dat->blog->title) . ')'; $comment->content_out = InputFilter::filter($dat->comment->content); $comment->date = $dat->comment->date; break; } if (!$comment) { continue; } $comment->status = Comment::STATUS_APPROVED; $comment->type = Comment::TRACKBACK; $comment->email = null; $backtype[] = $comment; } } Cache::set($cacheName, $backtype); return $backtype; }
/** * function act_comment_insert_before * This function is executed when the action "comment_insert_before" * is invoked from a Comment object. * The parent class, Plugin, handles registering the action * and hook name using the name of the function to determine * where it will be applied. * You can still register functions as hooks without using * this method, but boy, is it handy. * @param Comment The comment that will be processed before storing it in the database. **/ function action_comment_insert_before ( $comment ) { // This plugin ignores non-comments if ($comment->type != Comment::COMMENT) { return; } $spamcheck = array(); // <script> is bad, mmmkay? $comment->content = InputFilter::filter($comment->content); // first, check the commenter's name // if it's only digits, then we can discard this comment if ( preg_match( "/^\d+$/", $comment->name ) ) { $comment->status = Comment::STATUS_SPAM; $spamcheck[] = _t('Commenters with numeric names are spammy.'); } // now look at the comment text // if it's digits only, discard it $textonly = strip_tags( $comment->content ); if ( preg_match( "/^\d+$/", $textonly ) ) { $comment->status = Comment::STATUS_SPAM; $spamcheck[] = _t('Comments that are only numeric are spammy.'); } // is the content whitespaces only? if ( preg_match( "/\A\s+\z/", $textonly ) ) { $comment->status = Comment::STATUS_SPAM; $spamcheck[] = _t('Comments that are only whitespace characters are spammy.'); } // is the content the single word "array"? if ( 'array' == strtolower( $textonly ) ) { $comment->status = Comment::STATUS_SPAM; $spamcheck[] = _t('Comments that are only "array" are spammy.'); } // is the content the same as the name? if ( strtolower( $textonly ) == strtolower( $comment->name ) ) { $comment->status = Comment::STATUS_SPAM; $spamcheck[] = _t('Comments that consist of only the commenters name are spammy.'); } // a lot of spam starts with "<strong>some text...</strong>" if ( preg_match( "#^<strong>[^.]+\.\.\.</strong>#", $comment->content ) ) { $comment->status = Comment::STATUS_SPAM; $spamcheck[] = _t('Comments that start with strong text are spammy.'); } // are there more than 3 URLs posted? If so, it's almost certainly spam if ( preg_match_all( "#https?://#", strtolower( $comment->content ), $matches, PREG_SET_ORDER ) > 3 ) { $comment->status = Comment::STATUS_SPAM; $spamcheck[] = _t('There is a 3 URL limit in comments.'); } // are there more than 3 URLencoded characters in the content? if ( preg_match_all( "/%[0-9a-f]{2}/", strtolower( $comment->content ), $matches, PREG_SET_ORDER ) > 3 ) { $comment->status = Comment::STATUS_SPAM; $spamcheck[] = _t('There is a 3 URL-encoded character limit in comments.'); } // Was the tcount high enough? /* // This only works with special javascript running on comment form if ( empty($handlervars['tcount']) || $handlervars['tcount'] < 10 ) { $comment->status = Comment::STATUS_SPAM; $spamcheck[] = _t('Commenter did not actually type content.'); } */ // We don't allow bbcode here, silly if ( stripos($comment->content, '[url=') !== false ) { $comment->status = Comment::STATUS_SPAM; $spamcheck[] = _t('We do not accept BBCode here.'); } // Must have less than half link content $nonacontent = strip_tags(preg_replace('/<a.*?<\/a/i', '', $comment->content)); $text_length = strlen( $textonly ); if ( strlen($nonacontent) / ( $text_length == 0 ? 1 : $text_length) < 0.5 ) { $comment->status = Comment::STATUS_SPAM; $spamcheck[] = _t('Too much text that is a link compared to that which is not.'); } // Only do db checks if it's not already spam if ($comment->status != Comment::STATUS_SPAM) { $spams = DB::get_value('SELECT count(*) FROM ' . DB::table('comments') . ' WHERE status = ? AND ip = ?', array(Comment::STATUS_SPAM, $comment->ip)); // If you've already got two spams on your IP address, all you ever do is spam if ($spams > 1) { $comment->status = Comment::STATUS_SPAM; $spamcheck[] = sprintf(_t('Too many existing spams from this IP: %s'), $comment->ip); } } // Any commenter that takes longer than the session timeout is automatically moderated if (!isset($_SESSION['comments_allowed']) || ! in_array(Controller::get_var('ccode'), $_SESSION['comments_allowed'])) { $comment->status = Comment::STATUS_UNAPPROVED; $spamcheck[] = _t("The commenter's session timed out."); } if ( isset($comment->info->spamcheck) && is_array($comment->info->spamcheck)) { $comment->info->spamcheck = array_unique(array_merge($comment->info->spamcheck, $spamcheck)); } else { $comment->info->spamcheck = $spamcheck; } // otherwise everything looks good // so continue processing the comment return; }
/** * Receive a Pingback via XMLRPC * @param array $params An array of XMLRPC parameters from the remote call * @return string The success state of the pingback */ public function xmlrpc_pingback__ping( $params ) { try { list( $source_uri, $target_uri )= $params; // This should really be done by an Habari core function $target_parse = InputFilter::parse_url( $target_uri ); $target_stub = $target_parse['path']; $base_url = Site::get_path( 'base', true ); if ( '/' != $base_url) { $target_stub = str_replace( $base_url, '', $target_stub ); } $target_stub = trim( $target_stub, '/' ); if ( strpos( $target_stub, '?' ) !== false ) { list( $target_stub, $query_string )= explode( '?', $target_stub ); } // Can this be used as a target? $target_slug = URL::parse( $target_stub )->named_arg_values['slug']; if ( $target_slug === false ) { throw new XMLRPCException( 33 ); } // Does the target exist? $target_post = Post::get( array( 'slug' => $target_slug ) ); if ( $target_post === false ) { throw new XMLRPCException( 32 ); } // Is comment allowed? if ( $target_post->info->comments_disabled ) { throw new XMLRPCException( 33 ); } // Is this Pingback already registered? if ( Comments::get( array( 'post_id' => $target_post->id, 'url' => $source_uri, 'type' => Comment::PINGBACK ) )->count() > 0 ) { throw new XMLRPCException( 48 ); } // Retrieve source contents try { $rr = new RemoteRequest( $source_uri ); $rr->execute(); if ( ! $rr->executed() ) { throw new XMLRPCException( 16 ); } $source_contents = $rr->get_response_body(); $headers = $rr->get_response_headers(); } catch ( XMLRPCException $e ) { // catch our special type of exception and re-throw it throw $e; } catch ( Exception $e ) { throw new XMLRPCException( -32300 ); } // Encoding is converted into internal encoding. // First, detect the source string's encoding $habari_encoding = strtoupper( MultiByte::hab_encoding() ); $source_encoding = 'Windows-1252'; // Is the charset in the headers? if ( isset( $headers['Content-Type'] ) && strpos( $headers['Content-Type'], 'charset' ) !== false ) { // This regex should be changed to meet the HTTP spec at some point if ( preg_match("/charset[\x09\x0A\x0C\x0D\x20]*=[\x09\x0A\x0C\x0D\x20]*('?)([A-Za-z0-9\-\_]+)\1/i", $headers['Content-Type'], $matches ) ) { $source_encoding = strtoupper( $matches[2] ); } } // Can we tell the charset from the stream itself? else if ( ( $enc = MultiByte::detect_bom_encoding( $source_contents ) ) !== false ) { $source_encoding = $enc; } // Is the charset in a meta tag? else if ( preg_match( "/<meta[^>]+charset[\x09\x0A\x0C\x0D\x20]*=[\x09\x0A\x0C\x0D\x20]*([\"']?)([A-Za-z0-9\-\_]+)\1/i", $source_contents, $matches ) ) { $source_encoding = strtoupper( $matches[2] ); if (in_array($source_encoding, array("UTF-16", "UTF-16BE", "UTF-16LE"))) { $source_encoding = "UTF-8"; } } // Then, convert the string $ret = MultiByte::convert_encoding( $source_contents, $habari_encoding, $source_encoding ); if ( $ret !== false ) { $source_contents = $ret; } // Find the page's title preg_match( '/<title>(.*)<\/title>/is', $source_contents, $matches ); $source_title = $matches[1]; // Find the reciprocal links and their context preg_match( '/<body[^>]*>(.+)<\/body>/is', $source_contents, $matches ); $source_contents_filtered = preg_replace( '/\s{2,}/is', ' ', strip_tags( $matches[1], '<a>' ) ); // Get rid of all the non-recriprocal links $ht = new HTMLTokenizer( trim( $source_contents_filtered ) ); $set = $ht->parse(); $all_links = $set->slice( 'a', array() ); $keep_links = $set->slice( 'a', array( 'href' => $target_uri ) ); $bad_links = array_diff( $all_links, $keep_links ); foreach( $bad_links as $link ) { $link->tokenize_replace( '' ); $set->replace_slice( $link ); } $source_contents_filtered = (string)$set; // Get the excerpt if ( !preg_match( '%.{0,100}?<a[^>]*?href\\s*=\\s*("|\'|)' . $target_uri . '\\1[^>]*?'.'>(.+?)</a>.{0,100}%s', $source_contents_filtered, $source_excerpt ) ) { throw new XMLRPCException( 17 ); } /** Sanitize Data */ $source_excerpt = '…' . InputFilter::filter( $source_excerpt[0] ) . '…'; $source_title = InputFilter::filter($source_title); $source_uri = InputFilter::filter($source_uri); /* Sanitize the URL */ if (!empty($source_uri)) { $parsed = InputFilter::parse_url( $source_uri ); if ( $parsed['is_relative'] ) { // guess if they meant to use an absolute link $parsed = InputFilter::parse_url( 'http://' . $source_uri ); if ( ! $parsed['is_error'] ) { $source_uri = InputFilter::glue_url( $parsed ); } else { // disallow relative URLs $source_uri = ''; } } if ( $parsed['is_pseudo'] || ( $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https' ) ) { // allow only http(s) URLs $source_uri = ''; } else { // reconstruct the URL from the error-tolerant parsing // http:moeffju.net/blog/ -> http://moeffju.net/blog/ $source_uri = InputFilter::glue_url( $parsed ); } } // Add a new pingback comment $pingback = new Comment( array( 'post_id' => $target_post->id, 'name' => $source_title, 'email' => '', 'url' => $source_uri, 'ip' => Utils::get_ip(), 'content' => $source_excerpt, 'status' => Comment::STATUS_UNAPPROVED, 'date' => HabariDateTime::date_create(), 'type' => Comment::PINGBACK, ) ); $pingback->insert(); // Respond to the Pingback return 'The pingback has been registered'; } catch ( XMLRPCException $e ) { $e->output_fault_xml(); } }
/** * action: ajax_amazon_search * * @access public * @return void */ public function action_before_act_admin_ajax() { $handler_vars = Controller::get_handler_vars(); switch ($handler_vars['context']) { case 'amazon_search': if (empty($handler_vars['keywords'])) { echo json_encode(array('errorMessage' => _t('please specify keywords.'))); die; } if (empty($handler_vars['search_index'])) { echo json_encode(array('errorMessage' => _t('please specify searchIndex.'))); die; } $keywords = InputFilter::filter($handler_vars['keywords']); $search_index = InputFilter::filter($handler_vars['search_index']); if (empty($handler_vars['page'])) { $page = 1; } else { $page = InputFilter::filter($handler_vars['page']); } $result = $this->item_search($keywords, $search_index, $page); $xml = simplexml_load_string($result); if ((string) $xml->Items->Request->IsValid != 'True') { echo json_encode(array('errorMessage' => _t('following error reply existed from the server: ', 'amazon') . (string) $xml->Items->Request->Errors->Error->Message)); die; } if ((int) $xml->Items->TotalResults == 0) { echo json_encode(array('errorMessage' => _t('did not match any items.', 'amazon'))); die; } $output = array(); $output['TotalResults'] = (int) $xml->Items->TotalResults; $output['TotalPages'] = (int) $xml->Items->TotalPages; $output['CurrentPage'] = $page; $output['Start'] = ($page - 1) * 10 + 1; $output['End'] = $output['Start'] + 10; if ($output['End'] > $output['TotalResults']) { $output['End'] = $output['TotalResults']; } $output['HasPrev'] = false; $output['HasNext'] = false; if ($page != 1) { $output['HasPrev'] = true; } if ($page < $output['TotalPages']) { $output['HasNext'] = true; } $output['Items'] = array(); for ($i = 0; $i < count($xml->Items->Item); $i++) { $item = array(); $item['ASIN'] = (string) $xml->Items->Item[$i]->ASIN; $item['DetailPageURL'] = (string) $xml->Items->Item[$i]->DetailPageURL; $item['SmallImageURL'] = (string) $xml->Items->Item[$i]->SmallImage->URL; $item['SmallImageWidth'] = (int) $xml->Items->Item[$i]->SmallImage->Width; $item['SmallImageHeight'] = (int) $xml->Items->Item[$i]->SmallImage->Height; $item['Title'] = (string) $xml->Items->Item[$i]->ItemAttributes->Title; $item['Price'] = (string) $xml->Items->Item[$i]->ItemAttributes->ListPrice->FormattedPrice; $item['Binding'] = (string) $xml->Items->Item[$i]->ItemAttributes->Binding; $output['Items'][] = $item; } echo json_encode($output); die; case 'amazon_insert': if (empty($handler_vars['asin'])) { echo json_encode(array('errorMessage' => _t('please specify ASIN.', 'amazon'))); die; } $asin = InputFilter::filter($handler_vars['asin']); $result = $this->item_lookup($asin); $xml = simplexml_load_string($result); if ((string) $xml->Items->Request->IsValid != 'True') { echo json_encode(array('errorMessage' => _t('following error reply existed from the server: ', 'amazon') . (string) $xml->Items->Request->Errors->Error->Message)); die; } $item =& $xml->Items->Item; ob_start(); $template = preg_replace('/[^A-Za-z0-9\\-\\_]/', '', Options::get('amazon__template')); include dirname($this->get_file()) . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . $template . '.php'; $output['html'] = ob_get_contents(); ob_end_clean(); echo json_encode($output); //echo $output['html']; die; default: break; } }
/** * Add a comment to the site * * @param mixed $post A Post object instance or Post object id * @param string $name The commenter's name * @param string $email The commenter's email address * @param string $url The commenter's website URL * @param string $content The comment content * @param array $extra An associative array of extra values that should be considered */ function add_comment($post, $name = null, $email = null, $url = null, $content = null, $extra = null) { if (is_numeric($post)) { $post = Post::get(array('id' => $post)); } if (!$post instanceof Post) { // Not sure what you're trying to pull here, but that's no good header('HTTP/1.1 403 Forbidden', true, 403); die; } // let's do some basic sanity checking on the submission if (1 == Options::get('comments_require_id') && (empty($name) || empty($email))) { Session::error(_t('Both name and e-mail address must be provided.')); } if (empty($content)) { Session::error(_t('You did not provide any content for your comment!')); } if (Session::has_errors()) { // save whatever was provided in session data Session::add_to_set('comment', $name, 'name'); Session::add_to_set('comment', $email, 'email'); Session::add_to_set('comment', $url, 'url'); Session::add_to_set('comment', $content, 'content'); // now send them back to the form Utils::redirect($post->permalink . '#respond'); } if ($post->info->comments_disabled) { // comments are disabled, so let's just send // them back to the post's permalink Session::error(_t('Comments on this post are disabled!')); Utils::redirect($post->permalink); } /* Sanitize data */ foreach (array('name', 'url', 'email', 'content') as $k) { ${$k} = InputFilter::filter(${$k}); } /* Sanitize the URL */ if (!empty($url)) { $parsed = InputFilter::parse_url($url); if ($parsed['is_relative']) { // guess if they meant to use an absolute link $parsed = InputFilter::parse_url('http://' . $url); if (!$parsed['is_error']) { $url = InputFilter::glue_url($parsed); } else { // disallow relative URLs $url = ''; } } if ($parsed['is_pseudo'] || $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https') { // allow only http(s) URLs $url = ''; } else { // reconstruct the URL from the error-tolerant parsing // http:moeffju.net/blog/ -> http://moeffju.net/blog/ $url = InputFilter::glue_url($parsed); } } if (preg_match('/^\\p{Z}*$/u', $content)) { Session::error(_t('Comment contains only whitespace/empty comment')); Utils::redirect($post->permalink); } /* Create comment object*/ $comment = new Comment(array('post_id' => $post->id, 'name' => $name, 'email' => $email, 'url' => $url, 'ip' => sprintf("%u", ip2long($_SERVER['REMOTE_ADDR'])), 'content' => $content, 'status' => Comment::STATUS_UNAPPROVED, 'date' => HabariDateTime::date_create(), 'type' => Comment::COMMENT)); // Should this really be here or in a default filter? // In any case, we should let plugins modify the status after we set it here. $user = User::identify(); if ($user->loggedin && $comment->email == $user->email) { $comment->status = Comment::STATUS_APPROVED; } // Users need to have permission to add comments if (!$user->can('comment')) { Session::error(_t('You do not have permission to create comments.')); Utils::redirect($post->permalink); } // Allow themes to work with comment hooks Themes::create(); // Allow plugins to change comment data and add commentinfo based on plugin-added form fields Plugins::act('comment_accepted', $comment, $this->handler_vars, $extra); $spam_rating = 0; $spam_rating = Plugins::filter('spam_filter', $spam_rating, $comment, $this->handler_vars, $extra); $comment->insert(); $anchor = ''; // If the comment was saved if ($comment->id && $comment->status != Comment::STATUS_SPAM) { $anchor = '#comment-' . $comment->id; // store in the user's session that this comment is pending moderation if ($comment->status == Comment::STATUS_UNAPPROVED) { Session::notice(_t('Your comment is pending moderation.'), 'comment_' . $comment->id); } // if no cookie exists, we should set one // but only if the user provided some details $cookie = 'comment_' . Options::get('GUID'); if (!User::identify()->loggedin && !isset($_COOKIE[$cookie]) && (!empty($name) || !empty($email) || !empty($url))) { $cookie_content = $comment->name . '#' . $comment->email . '#' . $comment->url; $site_url = Site::get_path('base', true); setcookie($cookie, $cookie_content, time() + 31536000, $site_url); } } // Return the commenter to the original page. Utils::redirect($post->permalink . $anchor); }
/** * Recursively filter array values and strings using InputFilter::filter() * * @param mixed $value A value to filter * @return mixes The filtered value */ protected function base_filter($value) { if (is_array($value)) { return array_map(array($this, 'base_filter'), $value); } elseif (is_string($value)) { return InputFilter::filter($value); } else { return $value; } }
/** * Receive a Pingback via XMLRPC * @param array $params An array of XMLRPC parameters from the remote call * @return string The success state of the pingback */ public function xmlrpc_pingback__ping($params) { try { list($source_uri, $target_uri) = $params; // This should really be done by an Habari core function $target_parse = InputFilter::parse_url($target_uri); $target_stub = $target_parse['path']; $base_url = Site::get_path('base', TRUE); if ('/' != $base_url) { $target_stub = str_replace($base_url, '', $target_stub); } $target_stub = trim($target_stub, '/'); if (strpos($target_stub, '?') !== FALSE) { list($target_stub, $query_string) = explode('?', $target_stub); } // Can this be used as a target? $target_slug = URL::parse($target_stub)->named_arg_values['slug']; if ($target_slug === FALSE) { throw new XMLRPCException(33); } // Does the target exist? $target_post = Post::get(array('slug' => $target_slug)); if ($target_post === FALSE) { throw new XMLRPCException(32); } // Is comment allowed? if ($target_post->info->comments_disabled) { throw new XMLRPCException(33); } // Is this Pingback already registered? if (Comments::get(array('post_id' => $target_post->id, 'url' => $source_uri, 'type' => Comment::PINGBACK))->count() > 0) { throw new XMLRPCException(48); } // Retrieve source contents $rr = new RemoteRequest($source_uri); $rr->execute(); if (!$rr->executed()) { throw new XMLRPCException(16); } $source_contents = $rr->get_response_body(); // encoding is converted into internal encoding. // @todo check BOM at beginning of file before checking for a charset attribute $habari_encoding = MultiByte::hab_encoding(); if (preg_match("/<meta[^>]+charset=([A-Za-z0-9\\-\\_]+)/i", $source_contents, $matches) !== FALSE && strtolower($habari_encoding) != strtolower($matches[1])) { $ret = MultiByte::convert_encoding($source_contents, $habari_encoding, $matches[1]); if ($ret !== FALSE) { $source_contents = $ret; } } // Find the page's title preg_match('/<title>(.*)<\\/title>/is', $source_contents, $matches); $source_title = $matches[1]; // Find the reciprocal links and their context preg_match('/<body[^>]*>(.+)<\\/body>/is', $source_contents, $matches); $source_contents_filtered = preg_replace('/\\s{2,}/is', ' ', strip_tags($matches[1], '<a>')); if (!preg_match('%.{0,100}?<a[^>]*?href\\s*=\\s*("|\'|)' . $target_uri . '\\1[^>]*?' . '>(.+?)</a>.{0,100}%s', $source_contents_filtered, $source_excerpt)) { throw new XMLRPCException(17); } /** Sanitize Data */ $source_excerpt = '...' . InputFilter::filter($source_excerpt[0]) . '...'; $source_title = InputFilter::filter($source_title); $source_uri = InputFilter::filter($source_uri); /* Sanitize the URL */ if (!empty($source_uri)) { $parsed = InputFilter::parse_url($source_uri); if ($parsed['is_relative']) { // guess if they meant to use an absolute link $parsed = InputFilter::parse_url('http://' . $source_uri); if (!$parsed['is_error']) { $source_uri = InputFilter::glue_url($parsed); } else { // disallow relative URLs $source_uri = ''; } } if ($parsed['is_pseudo'] || $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https') { // allow only http(s) URLs $source_uri = ''; } else { // reconstruct the URL from the error-tolerant parsing // http:moeffju.net/blog/ -> http://moeffju.net/blog/ $source_uri = InputFilter::glue_url($parsed); } } // Add a new pingback comment $pingback = new Comment(array('post_id' => $target_post->id, 'name' => $source_title, 'email' => '', 'url' => $source_uri, 'ip' => sprintf("%u", ip2long($_SERVER['REMOTE_ADDR'])), 'content' => $source_excerpt, 'status' => Comment::STATUS_UNAPPROVED, 'date' => HabariDateTime::date_create(), 'type' => Comment::PINGBACK)); $pingback->insert(); // Respond to the Pingback return 'The pingback has been registered'; } catch (XMLRPCException $e) { $e->output_fault_xml(); } }
/** * Prevent certain published content from causing problems. **/ function filter_post_content($content) { $newcontent = InputFilter::filter($content); if ($content != $newcontent) { Session::notice('Certain content is filtered from posts to maintain the integrity of the demo.', 'lockdown_plugin'); } return $newcontent; }
/** * function add_comment * adds a comment to a post, if the comment content is not NULL * @param array An associative array of content found in the $_POST array */ public function act_add_comment() { Utils::check_request_method(array('POST')); $defaults = array('name' => '', 'email' => '', 'url' => '', 'content' => ''); // We need to get the post anyway to redirect back to the post page. $post = Post::get(array('id' => $this->handler_vars['id'])); if (!$post) { // trying to comment on a non-existent post? Weirdo. header('HTTP/1.1 403 Forbidden', true, 403); die; } // make sure all our default values are set so we don't throw undefined index errors foreach ($defaults as $k => $v) { if (!isset($this->handler_vars[$k])) { $this->handler_vars[$k] = $v; } } // let's do some basic sanity checking on the submission if (1 == Options::get('comments_require_id') && (empty($this->handler_vars['name']) || empty($this->handler_vars['email']))) { Session::error(_t('Both name and e-mail address must be provided.')); } if (empty($this->handler_vars['content'])) { Session::error(_t('You did not provide any content for your comment!')); } if (Session::has_errors()) { // save whatever was provided in session data Session::add_to_set('comment', $this->handler_vars['name'], 'name'); Session::add_to_set('comment', $this->handler_vars['email'], 'email'); Session::add_to_set('comment', $this->handler_vars['url'], 'url'); Session::add_to_set('comment', $this->handler_vars['content'], 'content'); // now send them back to the form Utils::redirect($post->permalink . '#respond'); } if ($post->info->comments_disabled) { // comments are disabled, so let's just send // them back to the post's permalink Session::error(_t('Comments on this post are disabled!')); Utils::redirect($post->permalink); } /* Sanitize data */ foreach ($defaults as $k => $v) { $this->handler_vars[$k] = InputFilter::filter($this->handler_vars[$k]); } /* Sanitize the URL */ if (!empty($this->handler_vars['url'])) { $url = $this->handler_vars['url']; $parsed = InputFilter::parse_url($url); if ($parsed['is_relative']) { // guess if they meant to use an absolute link $parsed = InputFilter::parse_url('http://' . $url); if (!$parsed['is_error']) { $url = InputFilter::glue_url($parsed); } else { // disallow relative URLs $url = ''; } } if ($parsed['is_pseudo'] || $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https') { // allow only http(s) URLs $url = ''; } else { // reconstruct the URL from the error-tolerant parsing // http:moeffju.net/blog/ -> http://moeffju.net/blog/ $url = InputFilter::glue_url($parsed); } $this->handler_vars['url'] = $url; } if (preg_match('/^\\p{Z}*$/u', $this->handler_vars['content'])) { Session::error(_t('Comment contains only whitespace/empty comment')); Utils::redirect($post->permalink); } /* Create comment object*/ $comment = new Comment(array('post_id' => $this->handler_vars['id'], 'name' => $this->handler_vars['name'], 'email' => $this->handler_vars['email'], 'url' => $this->handler_vars['url'], 'ip' => sprintf("%u", ip2long($_SERVER['REMOTE_ADDR'])), 'content' => $this->handler_vars['content'], 'status' => Comment::STATUS_UNAPPROVED, 'date' => HabariDateTime::date_create(), 'type' => Comment::COMMENT)); // Should this really be here or in a default filter? // In any case, we should let plugins modify the status after we set it here. $user = User::identify(); if ($user->loggedin && $comment->email == $user->email) { $comment->status = Comment::STATUS_APPROVED; } // Allow themes to work with comment hooks Themes::create(); $spam_rating = 0; $spam_rating = Plugins::filter('spam_filter', $spam_rating, $comment, $this->handler_vars); $comment->insert(); $anchor = ''; // If the comment was saved if ($comment->id) { $anchor = '#comment-' . $comment->id; // store in the user's session that this comment is pending moderation if ($comment->status == Comment::STATUS_UNAPPROVED) { Session::notice(_t('Your comment is pending moderation.'), 'comment_' . $comment->id); } // if no cookie exists, we should set one // but only if the user provided some details $cookie = 'comment_' . Options::get('GUID'); if (!isset($_COOKIE[$cookie]) && (!empty($this->handler_vars['name']) || !empty($this->handler_vars['email']) || !empty($this->handler_vars['url']))) { $cookie_content = $comment->name . '#' . $comment->email . '#' . $comment->url; $site_url = Site::get_path('base', true); setcookie($cookie, $cookie_content, time() + 31536000, $site_url); } } // Return the commenter to the original page. Utils::redirect($post->permalink . $anchor); }
public static function comment_safe_markdown($content) { // filter the HTML, just as a normal comment would be filtered before saving to the database return InputFilter::filter(self::markdown($content)); }
/** * Import the <outline> data from simplexml obj. $xml->body. * @todo support tags/categories * @todo don't be strict on duplicate matching. */ private function import_opml(\SimpleXMLElement $xml) { if (!$xml->outline) { throw new \Exception('Not a valid OPML resource'); } $count = 0; foreach ($xml->outline as $outline) { $atts = (array) $outline->attributes(); $params = $this->map_opml_atts($atts['@attributes']); if (isset($params['url']) && isset($params['title'])) { if (count(Posts::get(array('all:info' => array('url' => $params['url'])))) >= 1) { continue; } $params = array_map(function ($a) { return InputFilter::filter($a); }, $params); extract($params); $user = User::identify(); $params = array('title' => $title, 'pubdate' => isset($pubdate) ? HabariDateTime::date_create($pubdate) : HabariDateTime::date_create(), 'updated' => isset($updated) ? HabariDateTime::date_create($updated) : HabariDateTime::date_create(), 'content' => isset($content) ? $content : '', 'status' => Post::status('published'), 'content_type' => Post::type(self::CONTENT_TYPE), 'user_id' => $user->id); $blog = Post::create($params); foreach ($this->info_fields as $field) { if (isset(${$field}) && ${$field}) { $blog->info->{$field} = ${$field}; } } $blog->update(); $count++; } if ($outline->outline) { $count += $this->import_opml($outline); } } return $count; }
/** * Add a comment to the site * * @param mixed $post A Post object instance or Post object id * @param string $name The commenter's name * @param string $email The commenter's email address * @param string $url The commenter's website URL * @param string $content The comment content * @param array $extra An associative array of extra values that should be considered */ function add_comment($post, $name = null, $email = null, $url = null, $content = null, $extra = null) { if (is_numeric($post)) { $post = Post::get(array('id' => $post)); } if (!$post instanceof Post) { // Not sure what you're trying to pull here, but that's no good header('HTTP/1.1 403 Forbidden', true, 403); die; } /* Sanitize data */ foreach (array('name', 'url', 'email', 'content') as $k) { ${$k} = InputFilter::filter(${$k}); } // there should never be any HTML in the name, so do some extra filtering on it $name = strip_tags(html_entity_decode($name, ENT_QUOTES, 'UTF-8')); /* Sanitize the URL */ if (!empty($url)) { $parsed = InputFilter::parse_url($url); if ($parsed['is_relative']) { // guess if they meant to use an absolute link $parsed = InputFilter::parse_url('http://' . $url); if (!$parsed['is_error']) { $url = InputFilter::glue_url($parsed); } else { // disallow relative URLs $url = ''; } } if ($parsed['is_pseudo'] || $parsed['scheme'] !== 'http' && $parsed['scheme'] !== 'https') { // allow only http(s) URLs $url = ''; } else { // reconstruct the URL from the error-tolerant parsing // http:moeffju.net/blog/ -> http://moeffju.net/blog/ $url = InputFilter::glue_url($parsed); } } /* Create comment object*/ $comment = new Comment(array('post_id' => $post->id, 'name' => $name, 'email' => $email, 'url' => $url, 'ip' => Utils::get_ip(), 'content' => $content, 'status' => Comment::status('approved'), 'date' => DateTime::create(), 'type' => Comment::type('comment'))); // Should this really be here or in a default filter? // In any case, we should let plugins modify the status after we set it here. $user = User::identify(); if ($user->loggedin && $comment->email == $user->email) { $comment->status = 'approved'; } // Allow themes to work with comment hooks Themes::create(); // Allow plugins to change comment data and add commentinfo based on plugin-added form fields Plugins::act('comment_accepted', $comment, $this->handler_vars, $extra); $spam_rating = 0; $spam_rating = Plugins::filter('spam_filter', $spam_rating, $comment, $this->handler_vars, $extra); if ($spam_rating >= Options::get('spam_percentage', 100)) { $comment->status = 'spam'; } $comment->insert(); $anchor = ''; // If the comment was saved if ($comment->id && $comment->status != 'spam') { $anchor = '#comment-' . $comment->id; // store in the user's session that this comment is pending moderation if ($comment->status == 'unapproved') { Session::notice(_t('Your comment is pending moderation.'), 'comment_' . $comment->id); } // if no cookie exists, we should set one // but only if the user provided some details $cookie_name = 'comment_' . Options::get('public-GUID'); // build the string we store for the cookie $cookie_content = implode('#', array($comment->name, $comment->email, $comment->url)); // if the user is not logged in and there is no cookie OR the cookie differs from the current set if (User::identify()->loggedin == false && (!isset($_COOKIE[$cookie_name]) || $_COOKIE[$cookie_name] != $cookie_content)) { // update the cookie setcookie($cookie_name, $cookie_content, time() + DateTime::YEAR, Site::get_path('base', true)); } } // Return the commenter to the original page. Utils::redirect($post->permalink . $anchor); }
public function filter_post_content($content, Post $post) { if ($post->info->password) { // if user logged in, show post // make sure it's not just the anonymous user! $user = User::identify(); if ($user instanceof User && $user != User::anonymous()) { return $content; } $session = Session::get_set('post_passwords', false); $token = Utils::crypt('42' . $post->info->password . $post->id . Options::get('GUID')); // if password was submitted verify it if (Controller::get_var('post_password') && Controller::get_var('post_password_id') == $post->id) { $pass = InputFilter::filter(Controller::get_var('post_password')); if (Utils::crypt($pass, $post->info->password)) { Session::add_to_set('post_passwords', $token, $post->id); $session[$post->id] = $token; } else { Session::error(_t('That password was incorrect.', 'postpass')); } } // if password is stored in session verify it if (isset($session[$post->id]) && $session[$post->id] == $token) { return $content; } else { $theme = Themes::create(); $theme->post = $post; return $theme->fetch('post_password_form'); } } else { return $content; } }