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 @@
+
+
+
+
+
+
+ ">
+
+
+
+
+
+
+
+
+
\ 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 @@
+
+
+
+
+
+
+ ">
+
+
+
+
+
+
+
+
\ 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 @@
+
+
+
+
+
+
+
+
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, "{$tag_name}", $start);
+
+ // nothing was found?
+ if($open_tag === false && $close_tag === false){
+ break;
+ }
+
+ //echo "open_tag: {$open_tag}, close_tag {$close_tag}\r\n";
+
+ // found OPEN tag
+ if($close_tag === false || ($open_tag !== false && $open_tag < $close_tag) ){
+ $open_count++;
+ $start = $open_tag + $tag_len + 1;
+
+ //echo "found open tag: ".substr($html, $open_tag, 20)." at {$open_tag} \r\n";
+
+ // found CLOSE tag
+ } else if($open_tag === false || ($close_tag !== false && $close_tag < $open_tag) ){
+ $open_count--;
+ $start = $close_tag + $tag_len + 2;
+
+ //echo "found close tag: ".substr($html, $close_tag, 20)." at {$close_tag} \r\n";
+ }
+ }
+
+ // something went wrong... don't bother returning anything
+ if($open_count != 0){
+ return false;
+ }
+
+ $outer_end = $close_tag + $tag_len + 3;
+ $inner_end = $close_tag;
+
+ return array(
+ 'outer_start' => $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("