diff --git a/config.php b/config.php new file mode 100644 index 0000000..96c2718 --- /dev/null +++ b/config.php @@ -0,0 +1,57 @@ + '', + // CURLOPT_CONNECTTIMEOUT => 5 +); + +//$config['replace_title'] = 'Google Search'; + +//$config['error_redirect'] = "https://unblockvideos.com/#error={error_msg}"; +//$config['index_redirect'] = 'https://unblockvideos.com/'; + +// $config['replace_icon'] = 'icon_url'; + +// this better be here other Config::load fails +return $config; + +?> \ No newline at end of file diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000..990f029 Binary files /dev/null and b/favicon.ico differ diff --git a/index.php b/index.php new file mode 100644 index 0000000..0d16573 --- /dev/null +++ b/index.php @@ -0,0 +1,145 @@ + Proxy::VERSION + )); + } else { + echo render_template("./templates/main.php", array('version' => Proxy::VERSION)); + } + + } + + exit; +} + +// decode q parameter to get the real URL +$url = url_decrypt($_GET['q']); + +$proxy = new Proxy(); + +// load plugins +foreach (Config::get('plugins', array()) as $plugin) { + + $plugin_class = $plugin . 'Plugin'; + + if (file_exists('./plugins/' . $plugin_class . '.php')) { + + // use user plugin from /plugins/ + require_once('./plugins/' . $plugin_class . '.php'); + + } else if (class_exists('\\Proxy\\Plugin\\' . $plugin_class)) { + + // does the native plugin from php-proxy package with such name exist? + $plugin_class = '\\Proxy\\Plugin\\' . $plugin_class; + } + + // otherwise plugin_class better be loaded already through composer.json and match namespace exactly \\Vendor\\Plugin\\SuperPlugin + $proxy->getEventDispatcher()->addSubscriber(new $plugin_class()); +} + +try { + + // request sent to index.php + $request = Request::createFromGlobals(); + + // remove all GET parameters such as ?q= + $request->get->clear(); + + // forward it to some other URL + $response = $proxy->forward($request, $url); + + // if that was a streaming response, then everything was already sent and script will be killed before it even reaches this line + $response->send(); + +} catch (Exception $ex) { + + // if the site is on server2.proxy.com then you may wish to redirect it back to proxy.com + if (Config::get("error_redirect")) { + + $url = render_string(Config::get("error_redirect"), array( + 'error_msg' => rawurlencode($ex->getMessage()) + )); + + // Cannot modify header information - headers already sent + header("HTTP/1.1 302 Found"); + header("Location: {$url}"); + + } else { + + echo render_template("./templates/main.php", array( + 'url' => $url, + 'error_msg' => $ex->getMessage(), + 'version' => Proxy::VERSION + )); + + } +} + +?> \ No newline at end of file diff --git a/language.php b/language.php new file mode 100644 index 0000000..70a0a36 --- /dev/null +++ b/language.php @@ -0,0 +1,9 @@ + + + + + + Please authenticate! + + + + + + + + + + + +
+
+ + + +
+ +
+ + + + + + + +$val) { + $lp = (USE_USERNAME ? $key : '') .'%'.$val; + if ($_COOKIE['verify'] == md5($lp)) { + $found = true; + // prolong timeout + if (TIMEOUT_CHECK_ACTIVITY) { + setcookie("verify", md5($lp), $timeout, '/'); + } + break; + } + } + if (!$found) { + showLoginPasswordProtect(""); + } +} +?> \ No newline at end of file diff --git a/plugins/TestPlugin.php b/plugins/TestPlugin.php new file mode 100644 index 0000000..5259bf1 --- /dev/null +++ b/plugins/TestPlugin.php @@ -0,0 +1,25 @@ + \ No newline at end of file diff --git a/plugins/UrlFormPlugin.php b/plugins/UrlFormPlugin.php new file mode 100644 index 0000000..800fc88 --- /dev/null +++ b/plugins/UrlFormPlugin.php @@ -0,0 +1,39 @@ +getUri(); + + // we attach url_form only if this is a html response + if(!is_html($response->headers->get('content-type'))){ + return; + } + + // this path would be relative to index.php that included it? + $url_form = render_template("./templates/url_form.php", array( + 'url' => $url + )); + + $output = $response->getContent(); + + // does the html page contain tag, if so insert our form right after tag starts + $output = preg_replace('@@is', '$0'.PHP_EOL.$url_form, $output, 1, $count); + + // tag was not found, just put the form at the top of the page + if($count == 0){ + $output = $url_form.$output; + } + + $response->setContent($output); + } +} + +?> \ No newline at end of file diff --git a/templates/main.php b/templates/main.php new file mode 100644 index 0000000..50d759d --- /dev/null +++ b/templates/main.php @@ -0,0 +1,84 @@ + + + + + <?php echo $config['website-name']; ?> + + "> + + + + + + +
+
+
+ + + +
+

+

+ +
+
+
+ + Google +
+

+

+ +
+ "/> +
+
+
+

+ + +

+
+
+
+ + +
+ + \ No newline at end of file diff --git a/templates/proxy.png b/templates/proxy.png new file mode 100644 index 0000000..6619d92 Binary files /dev/null and b/templates/proxy.png differ diff --git a/templates/tos.php b/templates/tos.php new file mode 100644 index 0000000..361cd1a --- /dev/null +++ b/templates/tos.php @@ -0,0 +1,30 @@ + + + + + <?php echo $config['website-name']; ?> + + "> + + + + + +
+
+
+
+

+

+

+ +
+
+
+
+ + \ No newline at end of file diff --git a/templates/url_form.php b/templates/url_form.php new file mode 100644 index 0000000..2bb09f4 --- /dev/null +++ b/templates/url_form.php @@ -0,0 +1,80 @@ + + + + + +
+ +
+ +
+ " onclick=" window.location.href='index.php'"> + + + "> +
+ +
+ +
+ + diff --git a/vendor/athlon1600/php-proxy-plugin-bundle b/vendor/athlon1600/php-proxy-plugin-bundle new file mode 160000 index 0000000..172202c --- /dev/null +++ b/vendor/athlon1600/php-proxy-plugin-bundle @@ -0,0 +1 @@ +Subproject commit 172202c9b7913dc397b0a2eeacb68984a73f5c6e diff --git a/vendor/athlon1600/php-proxy/.gitignore b/vendor/athlon1600/php-proxy/.gitignore new file mode 100644 index 0000000..fd24be7 --- /dev/null +++ b/vendor/athlon1600/php-proxy/.gitignore @@ -0,0 +1,3 @@ +/vendor/* +composer.lock +.htaccess \ No newline at end of file diff --git a/vendor/athlon1600/php-proxy/LICENSE b/vendor/athlon1600/php-proxy/LICENSE new file mode 100644 index 0000000..a8fc851 --- /dev/null +++ b/vendor/athlon1600/php-proxy/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/athlon1600/php-proxy/README.md b/vendor/athlon1600/php-proxy/README.md new file mode 100644 index 0000000..4c25f84 --- /dev/null +++ b/vendor/athlon1600/php-proxy/README.md @@ -0,0 +1,99 @@ +php-proxy +========= + +Proxy script built on PHP, Symfony and cURL. +This library borrows ideas from Glype, Jenssegers proxy, and Guzzle. + +PHP-Proxy Web Application +------- + +If you're looking for a **project** version of this script that functions as a Web Application similar to Glype, then visit +[**php-proxy-app**](https://github.com/Athlon1600/php-proxy-app) + +See this php-proxy in action: +UnblockVideos.com + +Installation +------- + +Install it using [Composer](http://getcomposer.org): + +```bash +composer require athlon1600/php-proxy +``` + +Example +-------- + +```php +require('vendor/autoload.php'); + +use Proxy\Http\Request; +use Proxy\Proxy; + +$request = Request::createFromGlobals(); + +$proxy = new Proxy(); + +$proxy->getEventDispatcher()->addListener('request.before_send', function($event){ + + $event['request']->headers->set('X-Forwarded-For', 'php-proxy'); + +}); + +$proxy->getEventDispatcher()->addListener('request.sent', function($event){ + + if($event['response']->getStatusCode() != 200){ + die("Bad status code!"); + } + +}); + +$proxy->getEventDispatcher()->addListener('request.complete', function($event){ + + $content = $event['response']->getContent(); + $content .= ''; + + $event['response']->setContent($content); + +}); + +$response = $proxy->forward($request, "https://www.yahoo.com"); + +// send the response back to the client +$response->send(); + +``` + +Plugin Example +-------- + +```php +namespace Proxy\Plugin; + +use Proxy\Plugin\AbstractPlugin; +use Proxy\Event\ProxyEvent; + +use Proxy\Html; + +class MultiSiteMatchPlugin extends AbstractPlugin { + + // Matches multiple domain names (abc.com, abc.de, abc.pl) using regex (you MUST use / character) + protected $url_pattern = '/^abc\.(com|de|pl)$/is'; + // Matches a single domain name + //protected $url_pattern = 'abc.com'; + + public function onCompleted(ProxyEvent $event){ + + $response = $event['response']; + + $html = $response->getContent(); + + // do your stuff here... + + $response->setContent($html); + } +} +``` + +Notice that you must use the **/** character for regexes on ```$url_pattern``` diff --git a/vendor/athlon1600/php-proxy/composer.json b/vendor/athlon1600/php-proxy/composer.json new file mode 100644 index 0000000..2bf58ab --- /dev/null +++ b/vendor/athlon1600/php-proxy/composer.json @@ -0,0 +1,21 @@ +{ + "name": "athlon1600/php-proxy", + "type": "library", + "keywords": ["php proxy", "proxy script", "php web proxy", "web proxy", "php proxy script"], + "homepage": "https://www.php-proxy.com/", + "require": { + "ext-curl": "*", + "symfony/event-dispatcher": "~3.2" + }, + "suggest": { + "predis/predis": "For caching purposes" + }, + "autoload": { + "psr-4": { + "Proxy\\": "src/" + }, + "files": [ + "src/helpers.php" + ] + } +} \ No newline at end of file diff --git a/vendor/athlon1600/php-proxy/src/Config.php b/vendor/athlon1600/php-proxy/src/Config.php new file mode 100644 index 0000000..8708b24 --- /dev/null +++ b/vendor/athlon1600/php-proxy/src/Config.php @@ -0,0 +1,39 @@ + \ No newline at end of file diff --git a/vendor/athlon1600/php-proxy/src/Event/ProxyEvent.php b/vendor/athlon1600/php-proxy/src/Event/ProxyEvent.php new file mode 100644 index 0000000..1cfddec --- /dev/null +++ b/vendor/athlon1600/php-proxy/src/Event/ProxyEvent.php @@ -0,0 +1,39 @@ +data = $data; + } + + public function offsetSet($offset, $value){ + + if(is_null($offset)) { + $this->data[] = $value; + } else { + $this->data[$offset] = $value; + } + } + + public function offsetExists($offset){ + return isset($this->data[$offset]); + } + + public function offsetUnset($offset){ + unset($this->data[$offset]); + } + + public function offsetGet($offset){ + return isset($this->data[$offset]) ? $this->data[$offset] : null; + } + +} + +?> \ No newline at end of file diff --git a/vendor/athlon1600/php-proxy/src/Html.php b/vendor/athlon1600/php-proxy/src/Html.php new file mode 100644 index 0000000..08c4c40 --- /dev/null +++ b/vendor/athlon1600/php-proxy/src/Html.php @@ -0,0 +1,182 @@ +]*>(.*?)<\s*\/\s*script\s*>/is', '', $html); + return $html; + } + + public static function remove_styles($html){ + $html = preg_replace('/<\s*style[^>]*>(.*?)<\s*\/\s*style\s*>/is', '', $html); + return $html; + } + + public static function remove_comments($html){ + return preg_replace('//s', '', $html); + } + + private static function find($selector, $html, $start_from = 0){ + + $html = substr($html, $start_from); + + $inner_start = 0; + $inner_end = 0; + + $pattern = '//'; + + if(substr($selector, 0, 1) == '#'){ + $pattern = '/<(\w+)[^>]+id="'.substr($selector, 1).'"[^>]*>/is'; + } else if(substr($selector, 0, 1) == '.'){ + $pattern = '/<(\w+)[^>]+class="'.substr($selector, 1).'"[^>]*>/is'; + } else { + return false; + } + + if(preg_match($pattern, $html, $matches, PREG_OFFSET_CAPTURE)){ + + $outer_start = $matches[0][1]; + $inner_start = $matches[0][1] + strlen($matches[0][0]); + + // tag stuff + $tag_name = $matches[1][0]; + $tag_len = strlen($tag_name); + + $run_count = 300; + + // "open" 0){ + + $open_tag = strpos($html, "<{$tag_name}", $start); + $close_tag = strpos($html, " $outer_start + $start_from, + 'inner_start' => $inner_start + $start_from, + 'inner_end' => $inner_end + $start_from, + 'outer_end' => $outer_end + $start_from + ); + } + + return false; + } + + public static function extract_inner($selector, $html){ + return self::extract($selector, $html, true); + } + + public static function extract_outer($selector, $html){ + return self::extract($selector, $html, false); + } + + private static function extract($selector, $html, $inner = false){ + + $pos = 0; + $limit = 300; + + $result = array(); + $data = false; + + do { + + $data = self::find($selector, $html, $pos); + + if($data){ + + $code = substr($html, $inner ? $data['inner_start'] : $data['outer_start'], + $inner ? $data['inner_end'] - $data['inner_start'] : $data['outer_end'] - $data['outer_start']); + + $result[] = $code; + $pos = $data['outer_end']; + } + + } while ($data && --$limit > 0); + + return $result; + } + + public static function remove($selector, $html){ + return self::replace($selector, '', $html, false); + } + + public static function replace_outer($selector, $replace, $html, &$matches = NULL){ + return self::replace($selector, $replace, $html, false, $matches); + } + + public static function replace_inner($selector, $replace, $html, &$matches = NULL){ + return self::replace($selector, $replace, $html, true, $matches); + } + + private static function replace($selector, $replace, $html, $replace_inner = false, &$matches = NULL){ + + $start_from = 0; + $limit = 300; + + $data = false; + $replace = (array)$replace; + + do { + + $data = self::find($selector, $html, $start_from); + + if($data){ + + $r = array_shift($replace); + + // from where to where will we be replacing? + $replace_space = $replace_inner ? $data['inner_end'] - $data['inner_start'] : $data['outer_end'] - $data['outer_start']; + $replace_len = strlen($r); + + if($matches !== NULL){ + $matches[] = substr($html, $replace_inner ? $data['inner_start'] : $data['outer_start'], $replace_space); + } + + $html = substr_replace($html, $r, $replace_inner ? $data['inner_start'] : $data['outer_start'], $replace_space); + + // next time we resume search at position right at the end of this element + $start_from = $data['outer_end'] + ($replace_len - $replace_space); + } + + } while ($data && --$limit > 0); + + return $html; + } +} + +?> \ No newline at end of file diff --git a/vendor/athlon1600/php-proxy/src/Http/ParamStore.php b/vendor/athlon1600/php-proxy/src/Http/ParamStore.php new file mode 100644 index 0000000..6485d79 --- /dev/null +++ b/vendor/athlon1600/php-proxy/src/Http/ParamStore.php @@ -0,0 +1,84 @@ +data = $parameters; + $this->case_sensitive = $case_sensitive; + } + + private function normalizeKey($key){ + return $this->case_sensitive ? $key : strtolower($key); + } + + public function set($key, $value, $replace = true){ + + $key = $this->normalizeKey($key); + + // replacing or does not have existing key filled yet + if($replace || !$this->has($key)){ + $this->data[$key] = $value; + } else { + + if(is_array($this->data[$key])){ + $this->data[$key][] = $value; + } else { + $this->data[$key] = array($this->data[$key], $value); + } + } + } + + public function replace(array $data){ + + // remove all existing items first + $this->clear(); + + foreach($data as $key => $value){ + $this->set($key, $value); + } + } + + public function remove($key){ + unset($this->data[$this->normalizeKey($key)]); + } + + public function clear(){ + $this->data = array(); + } + + public function has($key){ + return isset($this->data[$this->normalizeKey($key)]); + } + + public function get($key, $default = null){ + + $key = $this->normalizeKey($key); + + return $this->has($key) ? $this->data[$key] : $default; + } + + // Returns an array of all values currently stored + public function all(){ + return $this->data; + } + + public function __toString(){ + return json_encode($this->data, true); + } +} + +?> \ No newline at end of file diff --git a/vendor/athlon1600/php-proxy/src/Http/Request.php b/vendor/athlon1600/php-proxy/src/Http/Request.php new file mode 100644 index 0000000..a51561b --- /dev/null +++ b/vendor/athlon1600/php-proxy/src/Http/Request.php @@ -0,0 +1,362 @@ +params = new ParamStore(); + $this->headers = new ParamStore(); + + // http params + $this->post = new ParamStore(null, true); + $this->get = new ParamStore(null, true); + + $this->files = new ParamStore(null, true); + + $this->setMethod($method); + $this->setUrl($url); + $this->setBody($body); + + // make the request ready to be sent right from the start - prepare must be called manually from this point on if you ever add post or file parameters + $this->prepare(); + } + + /* + Does multiple things + - regenerate content body based on $post and $files parameters + - set content-type, content-length headers + - set transfer-encoding, expect headers + */ + public function prepare(){ + + /* + Any HTTP/1.1 message containing an entity-body SHOULD include a Content-Type header field defining the media type of that body. + http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.2.1 + */ + + // Must be a multipart request + if($this->files->all()){ + + $boundary = self::generateBoundary(); + + $this->prepared_body = Request::buildPostBody($this->post->all(), $this->files->all(), $boundary); + $this->headers->set('content-type', 'multipart/form-data; boundary='.$boundary); + + } else if($this->post->all()){ + + $this->prepared_body = http_build_query($this->post->all()); + $this->headers->set('content-type', 'application/x-www-form-urlencoded'); + + } else { + + $this->headers->set('content-type', $this->detectContentType($this->body)); + $this->prepared_body = $this->body; + } + + /* + The transfer-length of a message is the length of the message-body as it appears in the message; that is, after any transfer-codings have been applied. + http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4 + */ + + $len = strlen($this->prepared_body); + + if($len > 0){ + $this->headers->set('content-length', $len); + } else { + $this->headers->remove('content-length'); + $this->headers->remove('content-type'); + } + } + + public function __toString(){ + $str = $this->getMethod().' '.$this->getUrl().' HTTP/'.$this->getProtocolVersion()."\r\n"; + return $str.$this->getRawHeaders()."\r\n\r\n".$this->getRawBody(); + } + + public function setMethod($method){ + $this->method = strtoupper($method); + } + + public function getMethod(){ + return $this->method; + } + + // this was no longer working --- https://github.com/guzzle/psr7/blob/master/src/functions.php + public static function parseQuery($query){ + $result = array(); + parse_str($query, $result); + + return $result; + } + + public function setUrl($url){ + // remove hashtag - preg_replace so we don't have to check for its existence first - is it possible preserving hashtag? + $url = preg_replace('/#.*/', '', $url); + + // check if url has any query parameters + $query = parse_url($url, PHP_URL_QUERY); + + // remove it and add the query params to get collection + if($query){ + //$url = str_replace('?'.$query, '', $url); + $url = preg_replace('/\?.*/', '', $url); + + $result = self::parseQuery($query); + $this->get->replace($result); + } + + // url without query params - those will be appended later + $this->url = $url; + $this->headers->set('host', parse_url($url, PHP_URL_HOST)); + } + + public function getRawHeaders(){ + + $result = array(); + + $headers = $this->headers->all(); + + // Sort headers by name + //ksort($headers); + + // Turn this into name=value pairs + foreach($headers as $name => $values){ + + // could be an array if multiple headers are sent with the same name? + foreach( (array)$values as $value){ + $name = implode('-', array_map('ucfirst', explode('-', $name))); + $result[] = sprintf("%s: %s", $name, $value); + } + } + + return implode("\r\n", $result); + } + + public function getUrl(){ + + // does this URL have any query parameters? + if($this->get->all()){ + return $this->url.'?'.http_build_query($this->get->all()); + } + + return $this->url; + } + + public function getUri(){ + return call_user_func_array(array($this, "getUrl"), func_get_args()); + } + + public function setProtocolVersion($version){ + $this->protocol_version = $version; + } + + public function getProtocolVersion(){ + return $this->protocol_version; + } + + // Set raw contents of the body + // this will clear all the values currently stored in POST and FILES + // will be ignored during PREPARE if post or files contain any values + public function setBody($body, $content_type = false){ + + // clear old body data + $this->post->clear(); + $this->files->clear(); + + // is this form data? + if(is_array($body)){ + $body = http_build_query($body); + } + + $this->body = (string)$body; + + // plain text should be: text/plain; charset=UTF-8 + if($content_type){ + $this->headers->set('content-type', $content_type); + } + + // do it! + $this->prepare(); + } + + private static function generateBoundary(){ + return '-----'.md5(microtime().rand()); + } + + // can be $_POST and $_FILES + public static function buildPostBody($fields, $files, $boundary = null){ + + // the reason BODY part is not included in sprintf pattern is because of limits + $part_field = "--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n"; + $part_file = "--%s\r\nContent-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\nContent-Type: %s\r\n\r\n"; + + // each part should be preceeded by this line + if(!$boundary){ + $boundary = self::generateBoundary(); + } + + $body = ''; + + foreach($fields as $name => $value){ + $body .= sprintf($part_field, $boundary, $name, $value); + $body .= "{$value}\r\n"; + } + + // data better have [name, tmp_name, and optional type] + foreach($files as $name => $values) { + // Multiple files can be uploaded using different name for input. + // See http://php.net/manual/en/features.file-upload.multiple.php + if (!is_array($values['tmp_name'])) { + $multiValues = array_map(function ($a) { + return (array)$a; + }, $values); + $fieldName = $name; + } else { + $multiValues = $values; + $fieldName = "{$name}[]"; + } + + foreach (array_keys($multiValues['tmp_name']) as $key) { + + // There must be no error http://php.net/manual/en/features.file-upload.errors.php + if (!$multiValues['tmp_name'][$key] || $multiValues['error'][$key] !== 0 || !is_readable($multiValues['tmp_name'][$key])) { + continue; + } + + $body .= sprintf($part_file, $boundary, $fieldName, $multiValues['name'][$key], $multiValues['type'][$key]); + $body .= file_get_contents($multiValues['tmp_name'][$key]); + $body .= "\r\n"; + } + } + $body .= "--{$boundary}--\r\n\r\n"; + + return $body; + } + + private function detectContentType($data){ + + // http://www.w3.org/Protocols/rfc1341/4_Content-Type.html + + // If the media type remains unknown, the recipient SHOULD treat it as type "application/octet-stream". + $content_type = 'application/octet-stream'; + + if(preg_match('/^{\s*"[^"]+"\s*:/', $data)){ + $content_type = 'application/json'; + } else if(preg_match('/^(?:<\?xml[^?>]+\?>)\s*<[^>]+>/i', $data)){ + $content_type = 'application/xml'; + } else if(preg_match('/^[a-zA-Z0-9_.~-]+=[^&]*&/', $data)){ + $content_type = 'application/x-www-form-urlencoded'; + } + + return $content_type; + } + + // Returns a parsed version of the body + /* + public function getBody(){ + + // what is the content type? + $content_type = $this->headers->get('content-type', ''); + + switch($content_type){ + case 'application/x-www-form-urlencoded': + $result = array(); + mb_parse_str($this->body, $result); + return $result; + case 'application/json': + return json_decode($this->body); + case 'text/xml': + case 'application/xml': + case 'application/x-xml': + return simplexml_load_string($this->body); + } + + return null; + } + */ + + // Returns raw body string exactly as it appears in the HTTP request + public function getRawBody(){ + return $this->prepared_body; + } + + public static function createFromGlobals(){ + + $method = $_SERVER['REQUEST_METHOD']; + $scheme = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']) ? 'https' : 'http'; + + $url = $scheme.'://'. $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']; + + $request = new Request($method, $url); + + // fill in headers + foreach($_SERVER as $name => $value){ + + if(strpos($name, 'HTTP_') === 0){ + + $name = substr($name, 5); + $name = str_replace('_', ' ', $name); + $name = ucwords(strtolower($name)); + $name = str_replace(' ', '-', $name); + + $request->headers->set($name, $value); + } + } + + // for extra convenience + //$request->params->set('user-ip', $_SERVER['REMOTE_ADDR']); + + // will be empty if content-type is multipart + $input = file_get_contents("php://input"); + + if(count($_FILES) > 0){ + $request->post->replace($_POST); + $request->files->replace($_FILES); + } else if(count($_POST) > 0){ + $request->post->replace($_POST); + } else { + $request->setBody($input); + } + + // for extra convenience + $request->prepare(); + + return $request; + } +} + + +?> \ No newline at end of file diff --git a/vendor/athlon1600/php-proxy/src/Http/Response.php b/vendor/athlon1600/php-proxy/src/Http/Response.php new file mode 100644 index 0000000..e5075ea --- /dev/null +++ b/vendor/athlon1600/php-proxy/src/Http/Response.php @@ -0,0 +1,120 @@ + 'Continue', + 101 => 'Switching Protocols', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Time-out', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Large', + 415 => 'Unsupported Media Type', + 416 => 'Requested range not satisfiable', + 417 => 'Expectation Failed', + 429 => 'Too Many Requests', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Time-out', + 505 => 'Unsupported Version' + ); + + public $status; + + public $headers; + + private $content; + + // getHeaderLines + public function __construct($content = '', $status = 200, $headers = array()){ + + $this->headers = new ParamStore($headers); + + $this->setContent($content); + $this->setStatusCode($status); + } + + public function setStatusCode($code){ + $this->status = $code; + } + + public function getStatusCode(){ + return $this->status; + } + + public function getStatusText(){ + return $this->statusCodes[$this->getStatusCode()]; + } + + public function setContent($content){ + $this->content = (string)$content; + } + + public function getContent(){ + return $this->content; + } + + public function sendHeaders(){ + + if(headers_sent()){ + return; + } + + header(sprintf('HTTP/1.1 %s %s', $this->status, $this->getStatusText()), true, $this->status); + + foreach($this->headers->all() as $name => $value){ + + /* + Multiple message-header fields with the same field-name MAY be present in a message + if and only if the entire field-value for that header field is defined as a comma-separated list + http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 + */ + + $values = is_array($value) ? $value : array($value); + + // false = do not replace previous identical header + foreach($values as $value){ + header("{$name}: {$value}", false); + } + } + } + + public function send(){ + $this->sendHeaders(); + echo $this->content; + } + +} + +?> \ No newline at end of file diff --git a/vendor/athlon1600/php-proxy/src/Plugin/AbstractPlugin.php b/vendor/athlon1600/php-proxy/src/Plugin/AbstractPlugin.php new file mode 100644 index 0000000..405c33b --- /dev/null +++ b/vendor/athlon1600/php-proxy/src/Plugin/AbstractPlugin.php @@ -0,0 +1,80 @@ +getUri(); + + // url filter provided and current request url does not match it + if($this->url_pattern){ + if(strpos($this->url_pattern, '/') === 0){ + if(!preg_match($this->url_pattern, $url)) + return; + } + else + { + if(stripos($url, $this->url_pattern) === false) + return; + } + } + + switch($event_name){ + + case 'request.before_send': + $this->onBeforeRequest($event); + break; + + case 'request.sent': + $this->onHeadersReceived($event); + break; + + case 'curl.callback.write': + $this->onCurlWrite($event); + break; + + case 'request.complete': + $this->onCompleted($event); + break; + } + } + + // This method returns an array indexed by event names and whose values are either the method name to call + // or an array composed of the method name to call and a priority. + final public static function getSubscribedEvents(){ + return array( + 'request.before_send' => 'route', + 'request.sent' => 'route', + 'curl.callback.write' => 'route', + 'request.complete' => 'route' + ); + } +} + +?> diff --git a/vendor/athlon1600/php-proxy/src/Plugin/BlockListPlugin.php b/vendor/athlon1600/php-proxy/src/Plugin/BlockListPlugin.php new file mode 100644 index 0000000..f64669b --- /dev/null +++ b/vendor/athlon1600/php-proxy/src/Plugin/BlockListPlugin.php @@ -0,0 +1,57 @@ +getUrl(); + $url_host = parse_url($url, PHP_URL_HOST); + + $fnc_custom = Config::get('blocklist.custom'); + if(is_callable($fnc_custom)){ + + $ret = call_user_func($fnc_custom, compact('user_ip', 'user_ip_long', 'url', 'url_host') ); + if(!$ret){ + throw new \Exception("Error: Access Denied!"); + } + + return; + } + + /* + 1. Wildcard format: 1.2.3.* + 2. CIDR format: 1.2.3/24 OR 1.2.3.4/255.255.255.0 + 3. Start-End IP format: 1.2.3.0-1.2.3.255 + */ + $ip_match = false; + $action_block = true; + + if(Config::has('blocklist.ip_allow')){ + $ip_match = Config::get('blocklist.ip_allow'); + $action_block = false; + } else if(Config::has('blocklist.ip_block')){ + $ip_match = Config::get('blocklist.ip_block'); + } + + if($ip_match){ + $m = re_match($ip_match, $user_ip); + + // ip matched and we are in block_mode + // ip NOT matched and we are in allow mode + if( ($m && $action_block) || (!$m && !$action_block)){ + throw new \Exception("Error: Access denied!"); + } + } + } +} + +?> \ No newline at end of file diff --git a/vendor/athlon1600/php-proxy/src/Plugin/CookiePlugin.php b/vendor/athlon1600/php-proxy/src/Plugin/CookiePlugin.php new file mode 100644 index 0000000..0d69eff --- /dev/null +++ b/vendor/athlon1600/php-proxy/src/Plugin/CookiePlugin.php @@ -0,0 +1,132 @@ +headers->get("cookie"); + + // remove old cookie header and rewrite it + $request->headers->remove("cookie"); + + /* + When the user agent generates an HTTP request, the user agent MUST NOT attach more than one Cookie header field. + http://tools.ietf.org/html/rfc6265#section-5.4 + */ + $send_cookies = array(); + + // extract "proxy cookies" only + // A Proxy Cookie would have the following name: COOKIE_PREFIX_domain-it-belongs-to__cookie-name + if(preg_match_all('@pc_(.+?)__(.+?)=([^;]+)@', $http_cookie, $matches, PREG_SET_ORDER)){ + + foreach($matches as $match){ + + $cookie_name = $match[2]; + $cookie_value = $match[3]; + $cookie_domain = str_replace("_", ".", $match[1]); + + // what is the domain or our current URL? + $host = parse_url($request->getUri(), PHP_URL_HOST); + + // does this cookie belong to this domain? + // sometimes domain begins with a DOT indicating all subdomains - deprecated but still in use on some servers... + if(strpos($host, $cookie_domain) !== false){ + $send_cookies[] = $cookie_name.'='.$cookie_value; + } + } + } + + // do we have any cookies to send? + if($send_cookies){ + $request->headers->set('cookie', implode("; ", $send_cookies)); + } + } + + // cookies received from a target server via set-cookie should be rewritten + public function onHeadersReceived(ProxyEvent $event){ + + $request = $event['request']; + $response = $event['response']; + + // does the response send any cookies? + $set_cookie = $response->headers->get('set-cookie'); + + if($set_cookie){ + + // remove set-cookie header and reconstruct it differently + $response->headers->remove('set-cookie'); + + // loop through each set-cookie line + foreach( (array)$set_cookie as $line){ + + // parse cookie data as array from header line + $cookie = $this->parse_cookie($line, $request->getUri()); + + // construct a "proxy cookie" whose name includes the domain to which this cookie belongs to + // replace dots with underscores as cookie name can only contain alphanumeric and underscore + $cookie_name = sprintf("%s_%s__%s", self::COOKIE_PREFIX, str_replace('.', '_', $cookie['domain']), $cookie['name']); + + // append a simple name=value cookie to the header - no expiration date means that the cookie will be a session cookie + $event['response']->headers->set('set-cookie', $cookie_name.'='.$cookie['value'], false); + } + } + } + + // adapted from browserkit + private function parse_cookie($line, $url){ + + $host = parse_url($url, PHP_URL_HOST); + + $data = array( + 'name' => '', + 'value' => '', + 'domain' => $host, + 'path' => '/', + 'expires' => 0, + 'secure' => false, + 'httpOnly' => true + ); + + $line = preg_replace('/^Set-Cookie2?: /i', '', trim($line)); + + // there should be at least one name=value pair + $pairs = array_filter(array_map('trim', explode(';', $line))); + + foreach($pairs as $index => $comp){ + + $parts = explode('=', $comp, 2); + $key = trim($parts[0]); + + if(count($parts) == 1){ + + // secure; HttpOnly; == 1 + $data[$key] = true; + + } else { + + $value = trim($parts[1]); + + if($index == 0){ + $data['name'] = $key; + $data['value'] = $value; + } else { + $data[$key] = $value; + } + } + } + + return $data; + } +} + +?> \ No newline at end of file diff --git a/vendor/athlon1600/php-proxy/src/Plugin/HeaderRewritePlugin.php b/vendor/athlon1600/php-proxy/src/Plugin/HeaderRewritePlugin.php new file mode 100644 index 0000000..9eccdf3 --- /dev/null +++ b/vendor/athlon1600/php-proxy/src/Plugin/HeaderRewritePlugin.php @@ -0,0 +1,69 @@ +headers->set('accept-encoding', 'identity'); + + // mask proxy referer + $event['request']->headers->remove('referer'); + } + + function onHeadersReceived(ProxyEvent $event){ + + // so stupid... onCompleted won't be called on "streaming" responses + $response = $event['response']; + $request_url = $event['request']->getUri(); + + // proxify header location value + if($response->headers->has('location')){ + + $location = $response->headers->get('location'); + + // just in case this is a relative url like: /en + $response->headers->set('location', proxify_url($location, $request_url)); + } + + $code = $response->getStatusCode(); + $text = $response->getStatusText(); + + if($code >= 400 && $code <= 600){ + throw new \Exception("Error accessing resource: {$code} - {$text}"); + } + + // we need content-encoding (in case server refuses to serve it in plain text) + // content-length: final size of content sent to user may change via plugins, so it makes no sense to send old content-length + $forward_headers = array('content-type', 'zzzcontent-length', 'accept-ranges', 'content-range', 'content-disposition', 'location', 'set-cookie'); + + foreach($response->headers->all() as $name => $value){ + + // is this one of the headers we wish to forward back to the client? + if(!in_array($name, $forward_headers)){ + $response->headers->remove($name); + } + } + + if(!$response->headers->has('content-disposition')){ + + $url_path = parse_url($request_url, PHP_URL_PATH); + $filename = basename($url_path); + + $response->headers->set('Content-Disposition', 'filename="'.$filename.'"'); + } + + // do not ever cache our proxy pages! + $response->headers->set("cache-control", "no-cache, no-store, must-revalidate"); + $response->headers->set("pragma", "no-cache"); + $response->headers->set("expires", 0); + } + +} + +?> \ No newline at end of file diff --git a/vendor/athlon1600/php-proxy/src/Plugin/ProxifyPlugin.php b/vendor/athlon1600/php-proxy/src/Plugin/ProxifyPlugin.php new file mode 100644 index 0000000..0d29cdf --- /dev/null +++ b/vendor/athlon1600/php-proxy/src/Plugin/ProxifyPlugin.php @@ -0,0 +1,185 @@ +base_url), $matches[0]); + } + + // this.params.logoImg&&(e="background-image: url("+this.params.logoImg+")") + private function css_import($matches){ + return str_replace($matches[2], proxify_url($matches[2], $this->base_url), $matches[0]); + } + + // replace src= and href= + private function html_attr($matches){ + + // could be empty? + $url = trim($matches[2]); + + if(stripos($url, 'data:') === 0 || stripos($url, 'magnet:') === 0 ){ + return $matches[0]; + } + + return str_replace($url, proxify_url($url, $this->base_url), $matches[0]); + } + + private function form_action($matches){ + + // sometimes form action is empty - which means a postback to the current page + // $matches[1] holds single or double quote - whichever was used by webmaster + + // $matches[2] holds form submit URL - can be empty which in that case should be replaced with current URL + if(!$matches[2]){ + $matches[2] = $this->base_url; + } + + $new_action = proxify_url($matches[2], $this->base_url); + + // what is form method? + $form_post = preg_match('@method=(["\'])post\1@i', $matches[0]) == 1; + + // take entire form string - find real url and replace it with proxified url + $result = str_replace($matches[2], $new_action, $matches[0]); + + // must be converted to POST otherwise GET form would just start appending name=value pairs to your proxy url + if(!$form_post){ + + // may throw Duplicate Attribute warning but only first method matters + $result = str_replace("post->has('convertGET')){ + + // we don't need this parameter anymore + $request->post->remove('convertGET'); + + // replace all GET parameters with POST data + $request->get->replace($request->post->all()); + + // remove POST data + $request->post->clear(); + + // This is now a GET request + $request->setMethod('GET'); + + $request->prepare(); + } + } + + private function meta_refresh($matches){ + $url = $matches[2]; + return str_replace($url, proxify_url($url, $this->base_url), $matches[0]); + } + + // , <base>, <link>, <style>, <meta>, <script>, <noscript> + private function proxify_head($str){ + + // let's replace page titles with something custom + if(Config::get('replace_title')){ + $str = preg_replace('/<title[^>]*>(.*?)<\/title>/is', '<title>'.Config::get('replace_title').'', $str); + } + + + // base - update base_url contained in href - remove tag entirely + //$str = preg_replace_callback('/]*href= + + // link - replace href with proxified + // link rel="shortcut icon" - replace or remove + + // meta - only interested in http-equiv - replace url refresh + // + $str = preg_replace_callback('/content=(["\'])\d+\s*;\s*url=(.*?)\1/is', array($this, 'meta_refresh'), $str); + + return $str; + } + + // The background attribute is not supported in HTML5. Use CSS instead. + private function proxify_css($str){ + + // The HTML5 standard does not require quotes around attribute values. + + // if {1} is not there then youtube breaks for some reason + $str = preg_replace_callback('@[^a-z]{1}url\s*\((?:\'|"|)(.*?)(?:\'|"|)\)@im', array($this, 'css_url'), $str); + + // https://developer.mozilla.org/en-US/docs/Web/CSS/@import + // TODO: what about @import directives that are outside