/** * A validation function that returns an error if the value passed in is not a valid URL. * * @param string $text A string to test if it is a valid URL * @param FormControl $control The control that defines the value * @param FormContainer $form The container that holds the control * @param string $warning An optional error message * @return array An empty array if the string is a valid URL, or an array with strings describing the errors */ public static function validate_url($text, $control, $form, $warning = null, $schemes = array('http', 'https'), $guess = true) { if (!empty($text)) { $parsed = InputFilter::parse_url($text); if ($parsed['is_relative']) { if ($guess) { // guess if they meant to use an absolute link $parsed = InputFilter::parse_url('http://' . $text); if ($parsed['is_error']) { // disallow relative URLs $warning = empty($warning) ? _t('Relative urls are not allowed') : $warning; return array($warning); } else { $warning = empty($warning) ? _t('Relative urls are not allowed') : $warning; return array($warning); } } } if ($parsed['is_pseudo'] || !in_array($parsed['scheme'], $schemes)) { // allow only http(s) URLs $warning = empty($warning) ? _t('Only %s urls are allowed', array(Format::and_list($schemes))) : $warning; return array($warning); } } return array(); }
public function test_parse_url_sanitization_javascript() { $urls = array('javascript:alert(0);', 'javascript:alert(0);', 'java	script:alert(0);', '	javascript:alert(0);', 'java
script:alert(0);', '
javascript:alert(0);', 'java
script:alert(0);', '
javascript:alert(0);'); foreach ($urls as $url) { $url = html_entity_decode($url, null, 'UTF-8'); $parsed = InputFilter::parse_url($url); $this->assert_equal($parsed['scheme'], 'javascript', $url . ' != ' . $parsed['scheme']); } }
public function execute($method, $url, $headers, $body, $config) { $merged_headers = array(); foreach ($headers as $k => $v) { $merged_headers[] = $k . ': ' . $v; } // parse out the URL so we can refer to individual pieces $url_pieces = InputFilter::parse_url($url); // set up the options we'll use when creating the request's context $options = array('http' => array('method' => $method, 'header' => implode("\n", $merged_headers), 'timeout' => $config['timeout'], 'follow_location' => $this->can_followlocation, 'max_redirects' => $config['max_redirects'], 'verify_peer' => $config['ssl']['verify_peer'], 'cafile' => $config['ssl']['cafile'], 'capath' => $config['ssl']['capath'], 'local_cert' => $config['ssl']['local_cert'], 'passphrase' => $config['ssl']['passphrase'])); if ($method == 'POST') { $options['http']['content'] = $body; } if ($config['proxy']['server'] != '' && !in_array($url_pieces['host'], $config['proxy']['exceptions'])) { $proxy = $config['proxy']['server'] . ':' . $config['proxy']['port']; if ($config['proxy']['username'] != '') { $proxy = $config['proxy']['username'] . ':' . $config['proxy']['password'] . '@' . $proxy; } $options['http']['proxy'] = 'tcp://' . $proxy; } // create the context $context = stream_context_create($options); // perform the actual request - we use fopen so stream_get_meta_data works $fh = @fopen($url, 'r', false, $context); if ($fh === false) { throw new Exception(_t('Unable to connect to %s', array($url_pieces['host']))); } // read in all the contents -- this is the same as file_get_contens, only for a specific stream handle $body = stream_get_contents($fh); // get meta data $meta = stream_get_meta_data($fh); // close the connection before we do anything else fclose($fh); // did we timeout? if ($meta['timed_out'] == true) { throw new RemoteRequest_Timeout(_t('Request timed out')); } // $meta['wrapper_data'] should be a list of the headers, the same as is loaded into $http_response_header $headers = array(); foreach ($meta['wrapper_data'] as $header) { // break the header up into field and value $pieces = explode(': ', $header, 2); if (count($pieces) > 1) { // if the header was a key: value format, store it keyed in the array $headers[$pieces[0]] = $pieces[1]; } else { // some headers (like the HTTP version in use) aren't keyed, so just store it keyed as itself $headers[$pieces[0]] = $pieces[0]; } } $this->response_headers = $headers; $this->response_body = $body; $this->executed = true; return true; }
function test_url_parsing() { $this->assert_equal(InputFilter::parse_url('http://*****:*****@moeffju.net:8137/foo/bar?baz=quux#blah'), array('scheme' => 'http', 'host' => 'moeffju.net', 'port' => '8137', 'user' => 'hey', 'pass' => 'there', 'path' => '/foo/bar', 'query' => 'baz=quux', 'fragment' => 'blah', 'is_relative' => false, 'is_pseudo' => false, 'is_error' => false, 'pseudo_args' => '')); $this->assert_equal(InputFilter::parse_url('http://localhost/blog/'), array('scheme' => 'http', 'host' => 'localhost', 'port' => '', 'user' => '', 'pass' => '', 'path' => '/blog/', 'query' => '', 'fragment' => '', 'is_relative' => false, 'is_pseudo' => false, 'is_error' => false, 'pseudo_args' => '')); $this->assert_equal(InputFilter::parse_url('http:moeffju.net/blog/'), array('scheme' => 'http', 'host' => 'moeffju.net', 'port' => '', 'user' => '', 'pass' => '', 'path' => '/blog/', 'query' => '', 'fragment' => '', 'is_relative' => false, 'is_pseudo' => false, 'is_error' => false, 'pseudo_args' => '')); //$this->assert_equal(InputFilter::parse_url( 'file://Z:/Habari/User Manual/index.html' ), array ( 'scheme' => 'file', 'host' => '', 'port' => '', 'user' => '', 'pass' => '', 'path' => 'Z:/Habari/User Manual/index.html', 'query' => '', 'fragment' => '', 'is_relative' => false, 'is_pseudo' => false, 'is_error' => false, 'pseudo_args' => '', ) ); $this->assert_equal(InputFilter::parse_url('blog/'), array('scheme' => '', 'host' => '', 'port' => '', 'user' => '', 'pass' => '', 'path' => 'blog/', 'query' => '', 'fragment' => '', 'is_relative' => true, 'is_pseudo' => false, 'is_error' => false, 'pseudo_args' => '')); $this->assert_equal(InputFilter::parse_url('/furanzen/bla'), array('scheme' => '', 'host' => '', 'port' => '', 'user' => '', 'pass' => '', 'path' => '/furanzen/bla', 'query' => '', 'fragment' => '', 'is_relative' => true, 'is_pseudo' => false, 'is_error' => false, 'pseudo_args' => '')); $this->assert_equal(InputFilter::parse_url('?bla=barbaz&foo'), array('scheme' => '', 'host' => '', 'port' => '', 'user' => '', 'pass' => '', 'path' => '', 'query' => 'bla=barbaz&foo', 'fragment' => '', 'is_relative' => true, 'is_pseudo' => false, 'is_error' => false, 'pseudo_args' => '')); $this->assert_equal(InputFilter::parse_url('#'), array('scheme' => '', 'host' => '', 'port' => '', 'user' => '', 'pass' => '', 'path' => '', 'query' => '', 'fragment' => '', 'is_relative' => true, 'is_pseudo' => false, 'is_error' => false, 'pseudo_args' => '')); $this->assert_equal(InputFilter::parse_url('about:blank'), array('scheme' => 'about', 'host' => '', 'port' => '', 'user' => '', 'pass' => '', 'path' => '', 'query' => '', 'fragment' => '', 'is_relative' => false, 'is_pseudo' => true, 'is_error' => false, 'pseudo_args' => 'blank')); $this->assert_equal(InputFilter::parse_url('javascript:alert(document.cookie)'), array('scheme' => 'javascript', 'host' => '', 'port' => '', 'user' => '', 'pass' => '', 'path' => '', 'query' => '', 'fragment' => '', 'is_relative' => false, 'is_pseudo' => true, 'is_error' => false, 'pseudo_args' => 'alert(document.cookie)')); $this->assert_equal(InputFilter::parse_url('javascript:alert(\'/hey/there/foo?how=about#bar\')'), array('scheme' => 'javascript', 'host' => '', 'port' => '', 'user' => '', 'pass' => '', 'path' => '', 'query' => '', 'fragment' => '', 'is_relative' => false, 'is_pseudo' => true, 'is_error' => false, 'pseudo_args' => 'alert(\'/hey/there/foo?how=about#bar\')')); }
/** * 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); }
protected function get_local($file_name = '') { $parsed = InputFilter::parse_url($file_name); return HABARI_PATH . $parsed['path']; }
function upload( $photo, $title = '', $description = '', $tags = '', $perms = '', $async = 1, &$info = null ) { $store = HABARI_PATH . '/' . Site::get_path( 'user' ) . '/cache'; if ( !is_dir( $store ) ){ mkdir( $store, 0777 ); } $params = array( 'auth_token' => $this->cachedToken() ); $url = InputFilter::parse_url( 'file://' . $photo ); if ( isset( $url['scheme'] ) ){ $localphoto = fopen( HABARI_PATH . '/' . $photo, 'r' ); $store = tempnam( $store, 'G2F' ); file_put_contents( $store, $localphoto ); fclose( $localphoto ); $params['photo'] = $store; } else{ $params['photo'] = $photo; } $info = filesize( $params['photo'] ); if ( $title ){ $params['title'] = $title; } if ( $description ){ $params['description'] = $description; } if ( $tags ){ $params['tags'] = $tags; } if ( $perms ){ if ( isset( $perms['is_public'] ) ){ $params['is_public'] = $perms['is_public']; } if ( isset( $perms['is_friend'] ) ){ $params['is_friend'] = $perms['is_friend']; } if ( isset( $perms['is_family'] ) ){ $params['is_family'] = $perms['is_family']; } } if ( $async ){ $params['async'] = $async; } // call the upload method. $xml = $this->call( 'upload', $params ); if ( $store ){ unlink( $store ); } if ( Error::is_error( $xml ) ){ throw $xml; } if ( $async ){ return( (string)$xml->ticketid ); } else{ return( (string)$xml->photoid ); } }
/** * Merge query params from the URL with given params. * @param string $url The URL * @param string $params An associative array of parameters. */ private function merge_query_params($url, $params) { $urlparts = InputFilter::parse_url($url); if (!isset($urlparts['query'])) { $urlparts['query'] = ''; } if (!is_array($params)) { parse_str($params, $params); } $urlparts['query'] = http_build_query(array_merge(Utils::get_params($urlparts['query']), $params), '', '&'); return InputFilter::glue_url($urlparts); }
/** * get_dir returns a complete filesystem path to the requested item * 'config_file' returns the complete path to the config.php file, including the filename * 'config' returns the path of the directory containing config.php * 'user' returns the path of the user directory * 'theme' returns the path of the site's active theme * 'admin_theme' returns the path to the admin directory * 'vendor' returns the path to the vendor directory * @param string the name of the path item to return * @param bool whether to include a trailing slash. Default: No * @return string Path */ public static function get_dir($name, $trail = false) { $path = ''; switch (strtolower($name)) { case 'config_file': $path = Site::get_dir('config') . '/config.php'; break; case 'config': if (self::$config_path) { return self::$config_path; } self::$config_path = HABARI_PATH; $config_dirs = preg_replace('/^' . preg_quote(HABARI_PATH, '/') . '\\/user\\/sites\\/(.*)/', '$1', Utils::glob(HABARI_PATH . '/user/sites/*', GLOB_ONLYDIR)); if (empty($config_dirs)) { return self::$config_path; } $server = InputFilter::parse_url(Site::get_url('habari')); $request = array(); if (isset($server['port']) && $server['port'] != '' && $server['port'] != '80') { $request[] = $server['port']; } $request = array_merge($request, explode('.', $server['host'])); $basesegments = count($request); $request = array_merge($request, explode('/', trim($_SERVER['REQUEST_URI'], '/'))); $x = 0; do { $match = implode('.', $request); if (in_array($match, $config_dirs)) { self::$config_dir = $match; self::$config_path = HABARI_PATH . '/user/sites/' . self::$config_dir; self::$config_type = $basesegments > count($request) ? Site::CONFIG_SUBDOMAIN : Site::CONFIG_SUBDIR; self::$config_urldir = implode('/', array_slice($request, $basesegments)); break; } array_pop($request); $x--; if ($x < -10) { echo $x; var_dump($request); die('too many '); } } while (count($request) > 0); $path = self::$config_path; break; case 'user': if (Site::get_dir('config') == HABARI_PATH) { $path = HABARI_PATH . '/user'; } else { $path = Site::get_dir('config'); } break; case 'theme': $theme = Themes::get_theme_dir(); if (file_exists(Site::get_dir('config') . '/themes/' . $theme)) { $path = Site::get_dir('user') . '/themes/' . $theme; } elseif (file_exists(HABARI_PATH . '/user/themes/' . $theme)) { $path = HABARI_PATH . '/user/themes/' . $theme; } elseif (file_exists(HABARI_PATH . '/3rdparty/themes/' . $theme)) { $url = Site::get_url('habari') . '/3rdparty/themes/' . $theme; } else { $path = HABARI_PATH . '/system/themes/' . $theme; } break; case 'admin_theme': $path = HABARI_PATH . '/system/admin'; break; case 'vendor': $path = HABARI_PATH . '/system/vendor'; break; } $path .= Utils::trail($trail); $path = Plugins::filter('site_dir_' . $name, $path); return $path; }
/** * Scan all links in the content and send them a Pingback. * @param string $content The post content to search * @param string $source_uri The source of the content * @param Post $post The post object of the source of the ping * @param boolean $force If true, force the system to ping all links even if that had been pinged before */ public function pingback_all_links( $content, $source_uri, $post = NULL, $force = false ) { $tokenizer = new HTMLTokenizer( $content, false ); $tokens = $tokenizer->parse(); // slice out only A tags $slices = $tokens->slice( array( 'a' ), array( ) ); $urls = array(); foreach ( $slices as $slice ) { // if there is no href attribute, just skip it, though there is something wrong if ( !isset( $slice[0]['attrs']['href'] ) ) { continue; } else { $url = $slice[0]['attrs']['href']; } // make sure it's a valid URL before we waste our time $parsed = InputFilter::parse_url( $url ); if ( $parsed['is_error'] || $parsed['is_pseudo'] || $parsed['is_relative'] ) { continue; } else { $urls[] = $url; } } if ( is_object( $post ) && isset( $post->info->pingbacks_successful ) ) { $fn = ( $force === true ) ? 'array_merge' : 'array_diff'; $links = $fn( $urls, $post->info->pingbacks_successful ); } else { $links = $urls; } $links = array_unique( $links ); foreach ( $links as $target_uri ) { if ( $this->send_pingback( $source_uri, $target_uri, $post ) ) { EventLog::log( _t( 'Sent pingbacks for "%1$s", target: %2$s', array( $post->title, $target_uri ) ), 'info', 'Pingback' ); } } }
/** * @todo Does not honor timeouts on the actual request, only on the connect() call. */ private function _work($method, $urlbits, $headers, $body, $timeout) { $_errno = 0; $_errstr = ''; if (!isset($urlbits['port']) || $urlbits['port'] == 0) { $urlbits['port'] = 80; } $fp = @fsockopen($urlbits['host'], $urlbits['port'], $_errno, $_errstr, $timeout); if ($fp === FALSE) { return Error::raise(sprintf(_t('%s: Error %d: %s while connecting to %s:%d'), __CLASS__, $_errno, $_errstr, $urlbits['host'], $urlbits['port']), E_USER_WARNING); } // timeout to fsockopen() only applies for connecting stream_set_timeout($fp, $timeout); // fix headers $headers['Host'] = $urlbits['host']; $headers['Connection'] = 'close'; // merge headers into a list $merged_headers = array(); foreach ($headers as $k => $v) { $merged_headers[] = $k . ': ' . $v; } // build the request $request = array(); $resource = $urlbits['path']; if (isset($urlbits['query'])) { $resource .= '?' . $urlbits['query']; } $request[] = "{$method} {$resource} HTTP/1.1"; $request = array_merge($request, $merged_headers); $request[] = ''; if ($method === 'POST') { $request[] = $body; } $request[] = ''; $out = implode("\r\n", $request); if (!fwrite($fp, $out, strlen($out))) { return Error::raise(_t('Error writing to socket.')); } $in = ''; while (!feof($fp)) { $in .= fgets($fp, 1024); } fclose($fp); list($header, $body) = explode("\r\n\r\n", $in); // to make the following REs match $ correctly // and thus not break parse_url $header = str_replace("\r\n", "\n", $header); preg_match('|^HTTP/1\\.[01] ([1-5][0-9][0-9]) ?(.*)|', $header, $status_matches); if ($status_matches[1] == '301' || $status_matches[1] == '302') { if (preg_match('|^Location: (.+)$|mi', $header, $location_matches)) { $redirect_url = $location_matches[1]; $redirect_urlbits = InputFilter::parse_url($redirect_url); if (!isset($redirect_url['host'])) { $redirect_urlbits['host'] = $urlbits['host']; } $this->redir_count++; if ($this->redir_count > $this->max_redirs) { return Error::raise(_t('Maximum number of redirections exceeded.')); } return $this->_work($method, $redirect_urlbits, $headers, $body, $timeout); } else { return Error::raise(_t('Redirection response without Location: header.')); } } if (preg_match('|^Transfer-Encoding:.*chunked.*|mi', $header)) { $body = $this->_unchunk($body); } return array($header, $body); }
/** * Returns a humane commenter's link for a comment if a URL is supplied, or just display the comment author's name * * @param Theme $theme The current theme * @param Comment $comment The comment object * @return string A link to the comment author or the comment author's name with no link */ public function theme_comment_author_link($theme, $comment) { $url = $comment->url; if ($url != '') { $parsed_url = InputFilter::parse_url($url); if ($parsed_url['host'] == '') { $url = ''; } else { $url = InputFilter::glue_url($parsed_url); } } if ($url != '') { return '<a href="' . $url . '">' . $comment->name . '</a>'; } else { return $comment->name; } }
/** * get_dir returns a complete filesystem path to the requested item * 'config_file' returns the complete path to the config.php file, including the filename * 'config' returns the path of the directory containing config.php * 'user' returns the path of the user directory * 'theme' returns the path of the site's active theme * 'admin_theme' returns the path to the admin directory * 'vendor' returns the path to the vendor directory * @param string $name the name of the path item to return * @param bool|string $trail whether to include a trailing slash, or a string to use as the trailing value. Default: false * @return string Path */ public static function get_dir($name, $trail = false) { $path = ''; switch (strtolower($name)) { case 'config_file': $path = Site::get_dir('config') . '/config.php'; break; case 'config': if (self::$config_path) { return self::$config_path; } self::$config_path = HABARI_PATH; $config_dirs = preg_replace('/^' . preg_quote(HABARI_PATH, '/') . '\\/user\\/sites\\/(.*)/', '$1', Utils::glob(HABARI_PATH . '/user/sites/*', GLOB_ONLYDIR)); if (empty($config_dirs)) { return self::$config_path; } // Collect host parts $server = InputFilter::parse_url(Site::get_url('habari')); $request = array(); if (isset($server['port']) && $server['port'] != '' && $server['port'] != '80') { $request[] = $server['port']; } $request = array_merge($request, explode('.', $server['host'])); // Collect the subdirectory(s) the core is located in to determine the base path later // Don't add them to $request, they will be added with $_SERVER['REQUEST_URI'] $basesegments = count($request); if (!empty($server['path'])) { $coresubdir = explode('/', trim($server['path'], '/')); $basesegments += count($coresubdir); } // Preserve $request without directories for fallback $request_base = $request; // Add directory part if (!empty(trim($_SERVER['REQUEST_URI'], '/'))) { $subdirectories = explode('/', trim($_SERVER['REQUEST_URI'], '/')); $request = array_merge($request, $subdirectories); } // Now cut parts until we found a matching site directory $cuts = 0; $dir_cuts = 0; do { $match = implode('.', $request); if (in_array($match, $config_dirs)) { self::$config_dir = $match; self::$config_path = HABARI_PATH . '/user/sites/' . self::$config_dir; self::$config_type = isset($subdirectories) && array_intersect($subdirectories, $request) == $subdirectories ? Site::CONFIG_SUBDIR : Site::CONFIG_SUBDOMAIN; self::$config_urldir = implode('/', array_slice($request, $basesegments + $cuts)); break; } // Cut a part from the beginning array_shift($request); $cuts--; // Do not fallback to only the directory part, instead, cut one and start over if ($basesegments + $cuts <= 0 && isset($subdirectories) && $dir_cuts < count($subdirectories)) { $cuts = 0; $dir_cuts++; $request = array_merge($request_base, array_slice($subdirectories, 0, count($subdirectories) - $dir_cuts)); } // What does the following check do?! if ($cuts < -10) { echo $cuts; var_dump($request); die('too many '); } } while (count($request) > 0); $path = self::$config_path; break; case 'user': if (Site::get_dir('config') == HABARI_PATH) { $path = HABARI_PATH . '/user'; } else { $path = Site::get_dir('config'); } break; case 'theme': $theme = Themes::get_theme_dir(); if (file_exists(Site::get_dir('config') . '/themes/' . $theme)) { $path = Site::get_dir('user') . '/themes/' . $theme; } elseif (file_exists(HABARI_PATH . '/user/themes/' . $theme)) { $path = HABARI_PATH . '/user/themes/' . $theme; } elseif (file_exists(HABARI_PATH . '/3rdparty/themes/' . $theme)) { $path = Site::get_dir('habari') . '/3rdparty/themes/' . $theme; } else { $path = HABARI_PATH . '/system/themes/' . $theme; } break; case 'admin_theme': $path = HABARI_PATH . '/system/admin'; break; case 'vendor': $path = HABARI_PATH . '/system/vendor'; break; } $path .= Utils::trail($trail); $path = Plugins::filter('site_dir_' . $name, $path); return $path; }
/** * 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 function execute($method, $url, $headers, $body, $config) { $merged_headers = array(); foreach ($headers as $k => $v) { $merged_headers[] = $k . ': ' . $v; } $ch = curl_init(); $options = array(CURLOPT_URL => $url, CURLOPT_HEADERFUNCTION => array(&$this, '_headerfunction'), CURLOPT_MAXREDIRS => $config['max_redirects'], CURLOPT_CONNECTTIMEOUT => $config['connect_timeout'], CURLOPT_TIMEOUT => $config['timeout'], CURLOPT_SSL_VERIFYPEER => $config['ssl']['verify_peer'], CURLOPT_SSL_VERIFYHOST => $config['ssl']['verify_host'], CURLOPT_BUFFERSIZE => $config['buffer_size'], CURLOPT_HTTPHEADER => $merged_headers, CURLOPT_FAILONERROR => true, CURLOPT_ENCODING => ''); if ($this->can_followlocation && $config['follow_redirects']) { $options[CURLOPT_FOLLOWLOCATION] = true; // Follow 302's and the like. } switch ($method) { case 'POST': $options[CURLOPT_POST] = true; // POST mode. $options[CURLOPT_POSTFIELDS] = $body; break; case 'HEAD': $options[CURLOPT_HEADER] = true; $options[CURLOPT_NOBODY] = true; break; default: $options[CURLOPT_CRLF] = true; // Convert UNIX newlines to \r\n break; } // set proxy, if needed $urlbits = InputFilter::parse_url($url); if ($config['proxy']['server'] && !in_array($urlbits['host'], $config['proxy']['exceptions'])) { $options[CURLOPT_PROXY] = $config['proxy']['server'] . ':' . $config['proxy']['port']; // Validation of the existence of the port should take place in the Options form if ($config['proxy']['username']) { $options[CURLOPT_PROXYUSERPWD] = $config['proxy']['username'] . ':' . $config['proxy']['password']; switch ($config['proxy']['auth_type']) { case 'basic': $options[CURLOPT_PROXYAUTH] = CURLAUTH_BASIC; break; case 'digest': $options[CURLOPT_PROXYAUTH] = CURLAUTH_DIGEST; break; } } // if it's a socks proxy, we have to tell curl that if ($config['proxy']['type'] == 'socks') { $options[CURLOPT_PROXYTYPE] = CURLPROXY_SOCKS5; } } curl_setopt_array($ch, $options); /** * @todo Possibly find a way to generate a temp file without needing the user * to set write permissions on cache directory * * @todo Fallback to using the the old way if the cache directory isn't writable * * @todo How about trying to use the system-defined temp directory? We could at least try, even if safe_mode or something breaks it. - chrismeller */ $tmp = tempnam(FILE_CACHE_LOCATION, 'RR'); if (!$tmp) { throw new \Exception(_t('CURL Error. Unable to create temporary file name.')); } $fh = @fopen($tmp, 'w+b'); if (!$fh) { throw new \Exception(_t('CURL Error. Unable to open temporary file.')); } curl_setopt($ch, CURLOPT_FILE, $fh); $success = curl_exec($ch); if ($success) { rewind($fh); $body = stream_get_contents($fh); } fclose($fh); unset($fh); if (isset($tmp) && file_exists($tmp)) { unlink($tmp); } if (curl_errno($ch) !== 0) { // get the number and error before we close the handle so we don't error out $errno = curl_errno($ch); $error = curl_error($ch); // before we throw an exception, just to be nice curl_close($ch); switch ($errno) { case CURLE_OPERATION_TIMEOUTED: // the request timed out throw new RemoteRequest_Timeout($error, $errno); break; default: throw new \Exception(_t('CURL Error %1$d: %2$s', array($errno, $error))); break; } } curl_close($ch); $this->response_headers = $this->headers; // really redundant now, since we could write directly to response_headers, but makes it more clear where they came from $this->response_body = $body; $this->executed = true; return true; }
/** * Return & entities in a URL querystring to their previous & glory, for use in redirects * @param string $value A URL, maybe with a querystring * @return bool|string The valid, de-amped URL */ public static function de_amp($value) { $url = InputFilter::parse_url($value); $url['query'] = str_replace('&', '&', $url['query']); return InputFilter::glue_url($url); }
public function filter_comment_url_out($value, $comment) { $whitelist = $this->get_whitelist(); if (count($whitelist) > 0 && $comment->url != '') { $comment_url = InputFilter::parse_url($value); $domain = $comment_url['host']; if (in_array($domain, $whitelist)) { return $value; } } if (isset($comment->info->redirecturl)) { if ($comment->info->redirecturl == URLApprove::REDIRECT) { $value = URL::get('comment_url_redirect', array('id' => $comment->id, 'ccode' => $this->get_hash($comment->id))); } } elseif (Options::get('urlapprove__redirect_default') == true) { $value = URL::get('comment_url_redirect', array('id' => $comment->id, 'ccode' => $this->get_hash($comment->id))); } return $value; }
/** * get_dir returns a complete filesystem path to the requested item * 'config_file' returns the complete path to the config.php file, including the filename * 'config' returns the path of the directory containing config.php * 'user' returns the path of the user directory * 'theme' returns the path of the site's active theme * @param string the name of the path item to return * @param bool whether to include a trailing slash. Default: No * @return string Path */ public static function get_dir( $name, $trail = false ) { $path = ''; switch ( strtolower( $name ) ) { case 'config_file': $path = Site::get_dir( 'config' ) . '/config.php'; break; case 'config': if ( self::$config_path ) { return self::$config_path; } self::$config_path = HABARI_PATH; $config_dirs = preg_replace( '/^' . preg_quote( HABARI_PATH, '/' ) . '\/user\/sites\/(.*)\/config.php/', '$1', Utils::glob( HABARI_PATH . '/user/sites/*/config.php' ) ); if ( empty( $config_dirs ) ) { return self::$config_path; } $server = InputFilter::parse_url( Site::get_url( 'habari' ) ); $server = ( isset( $server['port'] ) ) ? $server['port'] . '.' . $server['host'] . '.' : $server['host'] . '.'; $request = explode( '/', trim( $_SERVER['REQUEST_URI'], '/' ) ); $match = trim( $server, '.' ); $x = count( $request ); do { if ( in_array( $match, $config_dirs ) ) { self::$config_dir = $match; self::$config_path = HABARI_PATH . '/user/sites/' . self::$config_dir; self::$config_type = ( $x > 0 ) ? Site::CONFIG_SUBDOMAIN : Site::CONFIG_SUBDIR; break; } $match = MultiByte::substr( $match, MultiByte::strpos( $match, '.' ) + 1 ); $x--; } while ( MultiByte::strpos( $match, '.' ) !== false ); $path = self::$config_path; break; case 'user': if ( Site::get_dir( 'config' ) == HABARI_PATH ) { $path = HABARI_PATH . '/user'; } else { $path = Site::get_dir( 'config' ); } break; case 'theme': $theme = Themes::get_theme_dir(); if ( file_exists( Site::get_dir( 'config' ) . '/themes/' . $theme ) ) { $path = Site::get_dir( 'user' ) . '/themes/' . $theme; } elseif ( file_exists( HABARI_PATH . '/user/themes/' . $theme ) ) { $path = HABARI_PATH . '/user/themes/' . $theme; } elseif ( file_exists( HABARI_PATH . '/3rdparty/themes/' . $theme ) ) { $url = Site::get_url( 'habari' ) . '/3rdparty/themes/' . $theme; } else { $path = HABARI_PATH . '/system/themes/' . $theme; } break; case 'admin_theme': $path = HABARI_PATH . '/system/admin'; break; case 'vendor': $path = HABARI_PATH . '/system/vendor'; break; } $path .= Utils::trail( $trail ); $path = Plugins::filter( 'site_dir_' . $name, $path ); return $path; }
/** * 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(); } }
/** * 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); }