public static function addGoogleAnalyticsId($id) { static $i; if (!isset($i)) { $i = 0; } $i++; $name = 'wmbp' . $i; $ga_script = <<<EOF <script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', '{$id}', 'auto', {'name': '{$name}'}); ga('{$name}.send', 'pageview'); </script> EOF; // Appends to main page / iframe container. RedirectWhenBlockedFull::appendToHtmlBody($ga_script); // Appends to iframed content. self::appendToHtmlBody($ga_script); }
function run() { if (count(RedirectWhenBlockedFull::getAltBaseUrls()) == 0) { $this->messages[] = 'Empty alt base urls'; return; } $domains = array(); foreach (RedirectWhenBlockedFull::getAltBaseUrls() as $url) { // Test DNS poisoning. $domain = parse_url($url, PHP_URL_HOST); $domains[] = $domain; } if (!Conf::$china_ip_for_dns_poisoning_test) { $this->messages[] = 'china_ip_for_dns_poisoning_test not set'; $this->n_failed++; } else { $command = ''; foreach ($domains as $domain) { $command .= '(dig +time=5 +tries=1 @' . Conf::$china_ip_for_dns_poisoning_test . ' ' . $domain . ' > /dev/null ; echo "' . $domain . ':"$?) & '; } $command = trim($command) . '& wait'; exec($command, $output); $n_domains_not_poisoned = 0; foreach ($output as $line) { $line_chunks = explode(':', $line, 2); if (count($line_chunks) == 2 && $line_chunks[0] && $line_chunks[1] >= 0) { list($domain, $dig_exit_code) = $line_chunks; if ($dig_exit_code == 0) { $this->messages[] = $domain . ' is poisoned (' . $dig_exit_code . ')'; } else { $this->messages[] = $domain . ' is not poisoned (' . $dig_exit_code . ')'; $n_domains_not_poisoned++; } } else { $this->messages[] = 'unknown dig result (' . $line . ')'; } } if (!$n_domains_not_poisoned) { $this->messages[] = 'no unpoisoned domains available'; $this->n_failed++; } } $alt_base_urls_file = dirname(dirname(__DIR__)) . '/rwb/conf/alt_base_urls.txt'; $alt_base_urls_modified_ago = time() - filemtime($alt_base_urls_file); $this->messages[] = "{$alt_base_urls_file} modified {$alt_base_urls_modified_ago} seconds ago"; if ($alt_base_urls_modified_ago > Conf::$alt_base_urls_modified_ago_max) { $this->messages[] = "{$alt_base_urls_file} expired"; return false; } return true; }
function getDownstreamOrigin() { static $downstream_origin_verified; if (!isset($downstream_origin_verified)) { $downstream_origin_verified = NULL; if (isset($_SERVER['HTTP_ORIGIN'])) { $downstream_origin = $_SERVER['HTTP_ORIGIN']; } elseif (isset($_SERVER['HTTP_REFERER'])) { $downstream_origin = http_build_scheme_host($_SERVER['HTTP_REFERER']); } if (isset($downstream_origin)) { foreach (RedirectWhenBlockedFull::getAltBaseUrls() as $alt_url_base) { if ($downstream_origin == http_build_scheme_host($alt_url_base)) { $downstream_origin_verified = $downstream_origin; break; } } } } return $downstream_origin_verified; }
<?php $apk_url = RedirectWhenBlockedFull::getBaseUrl() . '?' . RedirectWhenBlockedFull::QUERY_STRING_PARAM_NAME . '=' . Conf::OUTPUT_TYPE_APK; $url = 'https://chart.googleapis.com/chart?chs=200x200&cht=qr&chl=' . urlencode($apk_url) . '&choe=UTF-8'; $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $response = curl_exec($ch); curl_close($ch); if ($response) { header('Cache-Control: ' . getCacheControlHeader(60 * 60 * 24, 60 * 60 * 24 * 7, 60 * 60 * 24 * 7)); header('Content-Type: image/png'); print $response; }
public function getUrl() { static $url; if (!isset($url)) { if (isset($_GET[RedirectWhenBlockedFull::QUERY_STRING_PARAM_NAME]) && $_GET[RedirectWhenBlockedFull::QUERY_STRING_PARAM_NAME] == Conf::OUTPUT_TYPE_APK && Conf::$apk_url) { $url = Conf::$apk_url; $filename = basename(parse_url($url, PHP_URL_PATH)); header('Content-Disposition: attachment; filename=' . $filename); // Run after all other code to override other content-type header. register_shutdown_function(function () { header('Content-Type: application/vnd.android.package-archive'); }); } else { $url = RedirectWhenBlockedFull::getRequestUriWithoutQueryStringParam(); $this->removeThisScriptDirFromUrl($url); if (startsWith($url, '/http://') || startsWith($url, '/https://')) { $url = substr($url, 1); if (!TextExternalUrlFilters::matchesUrl($url)) { header('HTTP/1.0 403 Forbidden'); exit; } // If we for some reason have the default upstream host and scheme in the URL, remove them. $url_components = parse_url($url); if ($url_components['host'] == Conf::getDefaultUpstreamBaseUrlComponent('host') && $url_components['scheme'] == Conf::getDefaultUpstreamBaseUrlComponent('scheme')) { $new_url = http_build_path_query_fragment($url_components); $new_url = RedirectWhenBlockedFull::getBaseUrl() . ltrim($new_url, '/'); header('Location: ' . $new_url); exit; } // Use in DomUtlFilters for relative URLs. $base_url_suffix = rtrim(http_build_scheme_host($url), '/') . '/'; RedirectWhenBlockedFull::setBaseUrlSuffix($base_url_suffix); } else { if ($url == '/') { if (Conf::$default_upstream_url) { $url = Conf::$default_upstream_url; } } $url = Conf::$default_upstream_base_url . $url; } } } // Reverse rewrites of parameters inside URL. TextExternalUrlFilters::applyReverse($url); Log::add($url, 'url'); return $url; }
exit; } if ($_GET[RedirectWhenBlockedFull::QUERY_STRING_PARAM_NAME] == Conf::OUTPUT_TYPE_APK_URLS) { header('Content-Type: application/javascript'); $urls = array(); foreach (RedirectWhenBlockedFull::getAltBaseUrls() as $url) { $url .= '?' . RedirectWhenBlockedFull::QUERY_STRING_PARAM_NAME . '=' . Conf::OUTPUT_TYPE_APK; $urls[] = $url; } print json_encode($urls); exit; } if ($_GET[RedirectWhenBlockedFull::QUERY_STRING_PARAM_NAME] == Conf::OUTPUT_TYPE_APP_URLS) { header('Content-Type: application/javascript'); $urls = array(); foreach (RedirectWhenBlockedFull::getAltBaseUrls() as $url) { $url .= '?' . RedirectWhenBlockedFull::QUERY_STRING_PARAM_NAME . '=' . Conf::OUTPUT_TYPE_APP; $urls[] = $url; } print json_encode($urls); exit; } if ($_GET[RedirectWhenBlockedFull::QUERY_STRING_PARAM_NAME] == Conf::OUTPUT_TYPE_STATUS) { header('Cache-Control: max-age=0'); header('Content-Type: text/plain'); require 'status_tests/StatusTest.inc'; foreach (scandir('status_tests/enabled') as $file) { if ($file[0] == '.') { continue; } require 'status_tests/enabled/' . $file;
<?php if (!defined('WEBSITE_TITLE')) { define('WEBSITE_TITLE', 'Redirect When Blocked Full Edition Demo Website Index'); } require 'rwb/RedirectWhenBlockedFull.inc'; RedirectWhenBlockedFull::setUrlsFromConfDir(); RedirectWhenBlockedFull::setWebsiteTitle(WEBSITE_TITLE); RedirectWhenBlockedFull::run(); ?> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title><?php print WEBSITE_TITLE; ?> </title> </head> <body> <h1><?php print WEBSITE_TITLE; ?> </h1> <p>Current time: <?php print date('Y-m-d H:i:s'); ?> </p> <ul> <li><a href="index.php">Index</a></li> <li><a href="slow.php">Slow Page</a></li>
private static function getBaseUrlHostAndPath() { static $host_and_path; if (!isset($host_and_path)) { $base_url_components = parse_url(RedirectWhenBlockedFull::getBaseUrl()); $host_and_path = $base_url_components['host']; if (isset($base_url_components['path'])) { $host_and_path .= $base_url_components['path']; } } return $host_and_path; }
public static function setWebsiteTitle($website_title) { self::$website_title = $website_title; }
private function getBodyFilteredByContentType($body, $content_type) { switch ($content_type) { case self::CONTENT_TYPE_TEXT_CSS: $css_base_url = $this->getCssBaseUrl(); // Convert protocol relative URLs to HTTPS. $body = str_replace('url(//', 'url(https://', $body); $body = str_replace('url("//', 'url("https://', $body); $body = str_replace('url(\'//', 'url(\'https://', $body); // Disabling this part. Not sure why it was added in the first place. /* * $body = str_replace('url("../', 'url("' . $css_base_url . '../', * $body); * $body = str_replace('url(\'../', * 'url(\'' . $css_base_url . '../', $body); * $body = str_replace('url(../', 'url(' . $css_base_url . '../', * $body); */ $body = str_replace('url("/', 'url("' . $css_base_url, $body); $body = str_replace('url(\'/', 'url(\'' . $css_base_url, $body); $body = str_replace('url(/', 'url(' . $css_base_url, $body); break; case self::CONTENT_TYPE_TEXT_HTML: $dom = $this->getDomFromHtml($body); if ($dom) { foreach ($dom->find('head title') as $title_element) { $title = mb_convert_encoding_plus($title_element->text(), 'UTF-8', $this->getCharsetFromHeadersOrDom($dom)); if ($title) { $title .= ' | 免翻墙镜像'; // Update title in DOM. $title_element->innertext = mb_convert_encoding_plus($title, $this->getCharsetFromHeadersOrDom($dom), 'UTF-8'); break; } } } // Default title. if (!isset($title) || !$title) { $title = '免翻墙镜像'; } // Special - override meta redirects since RWB would just slow them down. if ($dom && count($dom->find('<META[http-equiv=refresh]'))) { foreach ($dom->find('<META[http-equiv=refresh]') as $meta) { $content = $meta->getAttribute('content'); if ($content) { $content_chunks = explode(';', $content, 2); if (isset($content_chunks[1])) { $url_chunks = explode('=', $content_chunks[1], 2); if (isset($url_chunks[1])) { $url = trim($url_chunks[1], '\'" '); if ($url) { $this->headers['Location'] = $url; return ''; } } } } } } if ($this->shouldUseRedirectWhenBlocked()) { RedirectWhenBlockedFull::setCharset($this->getCharsetFromHeadersOrDom($dom)); RedirectWhenBlockedFull::setWebsiteTitle($title); RedirectWhenBlockedFull::run(); if (RedirectWhenBlockedFull::getOutputType() == RedirectWhenBlockedFull::OUTPUT_TYPE_JSONP) { // RWB will turn output into JSONP. Remove content-type header to keep header sent by RWB. if (isset($this->headers['Content-Type'])) { unset($this->headers['Content-Type']); } // Also remove charset specification in inline HTML. if ($dom) { foreach ($dom->find('meta[http-equiv=Content-Type], meta[charset]') as $meta) { $meta->outertext = ''; } } } } // Might be for example JSONP data served with an incorrect header. if (!$dom || !$dom->find('body')) { break; } DomUrlFilters::applyAll($dom, $this->request); // Special for iframes. Prevent RWB from hijacking content (iframe in iframe problem). foreach ($dom->find('iframe') as $iframe) { $src = $iframe->getAttribute('src'); if ($src) { $src = http_add_query_component($src, RedirectWhenBlockedFull::QUERY_STRING_PARAM_NAME, RedirectWhenBlockedFull::OUTPUT_TYPE_IFRAME); $iframe->setAttribute('src', $src); } } // Special for style attributes - apply CSS filter. foreach ($dom->find('*[style]') as $style_element) { $style_element->setAttribute('style', $this->getBodyFilteredByContentType($style_element->getAttribute('style'), self::CONTENT_TYPE_TEXT_CSS)); } $dom_do_reset = false; // Don't insert stuff if request failed. if ($this->getResponseCode() == 200) { if (Conf::$html_body_appendix) { foreach ($dom->find('body') as $b) { $b->innertext .= Conf::$html_body_appendix; break; } $dom_do_reset = true; } if (Conf::$html_head_appendix) { foreach ($dom->find('head') as $h) { $h->innertext .= Conf::$html_head_appendix; break; } $dom_do_reset = true; } } if (count($dom->find('script[async]'))) { if ($dom_do_reset) { // Bug in Simple HTML Dom means that DOM has to be reloaded before further edits. $this->resetDom($dom); $dom_do_reset = false; } // Remove 'async' attributes because they break the page in IE11 when loaded in our iframe. foreach ($dom->find('script[async]') as $x) { $x->removeAttribute('async', ''); } } if (count($dom->find('style'))) { if ($dom_do_reset) { // Bug in Simple HTML Dom means that DOM has to be reloaded before further edits. $this->resetDom($dom); $dom_do_reset = false; } foreach ($dom->find('style') as $style) { $style->innertext = $this->getBodyFilteredByContentType($style->innertext, self::CONTENT_TYPE_TEXT_CSS); } } $body = $dom->__toString(); $this->destroyDom($dom); break; } return $body; }