123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434 |
- <?php
- /**
- * HSDN Mirroring Cacher
- *
- *
- * http://www.hsdn.org
- * ICQ: 980001, 988881
- *
- * @descr Зеркалирующий прокси-сервер
- * @author HSDN Team
- * @copyright Copyright (c) 2007-2017 Information Networks Ltd.
- * @version 2.2.3b
- */
- /*
- * ОПИСАНИЕ
- *
- * Данный класс позволяет создать зеркало любого удаленного ресурса на
- * локальном сервере. Созданное зеркало по сути является прокси сервером,
- * который будет непосредственно взаимодействовать с удаленным хостом.
- * Благодаря системе встроенного кэширования страниц и картинок, нет нужды
- * многократно загружать одну и ту же страницу с сервера.
- */
- /*
- * ВОЗМОЖНОСТИ
- *
- * - POST/GET и другие HTTP-запросы
- * - Кэширование страниц и картинок
- * - Возможность загрузки файлов
- * - Возможность передачи заголовков к удаленному хосту
- * - Поддержка Cookies и Сессий
- * - Возможность замены содержимого страниц ресурса
- * - Возможность назначать исключения из кэширования
- * - Возможность работы через HTTP-прокси
- * - Возможность передачи файлов на удаленный сервер
- */
- /*
- * СИСТЕМНЫЕ ТРЕБОВАНИЯ
- *
- * - PHP 5.2.0 или выше с поддержкой Сокетов
- * - Apache 1.3.0 или выше с поддержкой mod_rewrite
- */
- /*
- * ЛИЦЕНЗИЯ
- *
- * Данный скрипт распространяется по Универсальной общедоступной лицензии
- * GNU General Public License (GNU/GPL).
- *
- * Авторы не несут ответственность за любой ущерб, причиненный любой
- * стороне в результате использования этого скрипта.
- */
- /*
- * УСТАНОВКА
- *
- * 1. Скопируйте этот файл в корневой каталог на вашем хосте.
- *
- * 2. Создайте директорию `cache' и установите на нее права `0777'
- * (подробнее читайте описание команды `chmod').
- *
- * 3. Создайте файл с названием `cacher.php' и поместите в него скрипт
- * вызова данного класса (см. ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ).
- *
- * 4. Настройте необходимые переменные, указав адрес сайта, путь к
- * директории `cache' и т.д. (см. ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ).
- *
- * 5. Создайте и разместите в корневом каталоге файл `.htaccess' со
- * следующим содержанием:
- *
- DirectoryIndex cacher.php
- ErrorDocument 404 /cacher.php
- Options FollowSymLinks
- RewriteEngine on
- RewriteBase /
- RewriteRule ^(.*)$ cacher.php
- *
- * 6. Откройте в браузере ваш хост для просмотра результата работы
- * данного класса.
- */
- /*
- * СПРАВКА ПО ПЕРЕМЕННЫМ
- *
- * Настройки:
- * - cache_dir -- Директория хранения файлов кэша
- * - cache_time -- Период хранения кэша в часах (0 -- не кэшировать)
- * - custom_cache -- Выборочное кэширование
- * - forbidden -- Ограничение доступа к определенным ресурсам
- * - post_no_cache -- Не кэшировать POST-запросы
- * - ignore_sid -- Игнорировать SESSION ID в путях (URI)
- * - forwarded -- Отправлять заголовок X-Forwarded-For
- * - replace_ref -- Заменить хост в отправляемых заголовках HTTP_REFERER
- *
- * Соединение:
- * - connect -- Адрес подключения
- * - proxy_connect -- Адрес подключения через HTTP-прокси
- * - proxy_user -- Пользователь прокси-сервера
- * - proxy_pass -- Пароль прокси-сервера
- * - path -- Рабочий путь класса
- *
- * Обрабока:
- * - head_replace -- Автозамена фрагментов текста в заголовках
- * - head_cut -- Автоудаление фрагментов текста из теле заголовков
- * - body_replace -- Автозамена фрагментов текста в теле страницы
- * - body_cut -- Автоудаление фрагментов текста из теле страницы
- */
- /*
- * ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ
- *
- * Для кэширования одного сайта:
- *
- <?php
- include 'mcacher.class.php'; // Подключение класса
- $mcp = new Cacher; // Определение класса
- $mcp->cache_dir = 'cache'; // Директория хранения файлов кэша
- $mcp->cache_time = 24; // Период хранения кэша в часах (0 -- не кэшировать)
- // Пути, для которых следует применить индивидуальный период хранения кэша
- $mcp->custom_cache = array
- (
- "^/news" => 0, // Не кэшировать страницу /news
- ".dhtml$" => 4 // Обновлять кэш для файлов .dhtml раз в 4 часа
- );
- // Ограничение доступа к различным ресурсам, страницам или файлам
- $mcp->forbidden = array
- (
- "^/badpage.php", // Закрыть доступ к странице /badpage.php
- ".mp3$" // Закрыть доступ ко всем файлам .mp3
- );
- $mcp->connect = 'http://www.hsdn.org'; // Адрес подключения
- // Автозамена реального имени сайта на имя сайта зеркала в заголовках HTTP
- $mcp->head_replace = array
- (
- 'www.hsdn.org' => $_SERVER['SERVER_NAME'] // Для заголовков, пр. Location
- );
- // Автозамена реального имени сайта на имя сайта зеркала в теле страницы,
- // а также в ссылках на страницах
- $mcp->body_replace = array
- (
- 'http://www.hsdn.org' => '' // Заменяем ссылки вида:
- // href="http://www.hsdn.org/link" на href="/link"
- );
- $mcp->run_cache(); // Запуск класса
- *
- * Для кэширования нескольких сайтов:
- *
- <?php
- include 'mcacher.class.php'; // Подключение класса
- $mcp = new Cacher; // Определение класса
- $path = $mcp->uri_array(); // Получение аргументов URI
- // Выбор сайта по первому аргументу URI
- switch($path[1])
- {
- // Первый сайт (доступен как: http://my-cache.ru/www.hsdn.org/)
- case 'www.hsdn.org':
- $mcp->cache_dir = 'cache/www.hsdn.org'; // Директория хранения файлов кэша
- $mcp->cache_time = 24; // Период хранения кэша в часах (0 -- не кэшировать)
- $mcp->connect = 'http://www.hsdn.org'; // Адрес подключения
- $mcp->path = '/www.hsdn.org'; // Рабочий путь класса
- // Возможно, придется заменить некоторые ссылки
- $mcp->body_replace = array
- (
- '="/' => '="'.$mcp->path.'/', // Заменяем ссылки вида:
- // href="/link" на href="/www.hsdn.org/link"
- );
- // ... ниже можно определить остальные переменные, как в первом примере
- break;
- // Второй сайт (доступен как: http://my-cache.ru/www.ya.ru/)
- case 'www.ya.ru':
- $mcp->cache_dir = 'cache/www.ya.ru'; // Директория хранения файлов кэша
- $mcp->cache_time = 0; // Период хранения кэша в часах (0 -- не кэшировать)
- $mcp->connect = 'http://www.ya.ru'; // Адрес подключения
- $mcp->path = '/www.ya.ru'; // Рабочий путь класса
- // Возможно, придется заменить некоторые ссылки
- $mcp->body_replace = array
- (
- '="/' => '="'.$mcp->path.'/', // Заменяем ссылки вида:
- // href="/link" на href="/www.ya.ru/link"
- );
- // ... ниже можно определить остальные переменные, как в первом примере
- break;
- // Сайт по-умолчанию (доступен как: http://my-cache.ru/)
- default:
- $mcp->cache_dir = 'cache'; // Директория хранения файлов кэша
- $mcp->cache_time = 0; // Период хранения кэша в часах (0 -- не кэшировать)
- $mcp->connect = 'http://www.mail.ru'; // Адрес подключения
- // ... ниже можно определить остальные переменные, как в первом примере
- break;
- }
- $mcp->run_cache(); // Запуск класса
- *
- */
- /*
- * Версия класса
- */
- define('MCP_VERSION', '2.2.3b');
- /**
- * Класс Cacher
- *
- * @author HSDN Team
- */
- class Cacher
- {
- /*
- * Хост и порт подключения
- *
- * @access public
- * @var string
- */
- var $connect = null;
- /*
- * Хост подключения
- *
- * @access private
- * @var string
- */
- var $host = null;
- /*
- * Порт подключения
- *
- * @access private
- * @var int
- */
- var $port = null;
- /*
- * Схема запроса
- *
- * @access private
- * @var string
- */
- var $scheme = null;
- /*
- * Рабочий путь класса
- *
- * @access public
- * @var string
- */
- var $path = null;
- /*
- * Хост и порт подключения через прокси
- *
- * @access public
- * @var string
- */
- var $proxy_connect = null;
- /*
- * Хост подключения через прокси
- *
- * @access private
- * @var string
- */
- var $proxy_host = null;
- /*
- * Порт подключения через прокси
- *
- * @access private
- * @var int
- */
- var $proxy_port = null;
- /*
- * Пользователь прокси-сервера
- *
- * @access public
- * @var string
- */
- var $proxy_user = null;
- /*
- * Пароль прокси-сервера
- *
- * @access public
- * @var string
- */
- var $proxy_pass = null;
- /*
- * Директория хранения файлов кэша
- *
- * @access public
- * @var string
- */
- var $cache_dir = null;
- /*
- * Период хранения кэша
- *
- * @access public
- * @var int
- */
- var $cache_time = null;
- /*
- * Массив индивидуального кэширования
- *
- * @access public
- * @var array
- */
- var $custom_cache = array();
- /*
- * Массив запрета доступа к ресурсам
- *
- * @access public
- * @var array
- */
- var $forbidden = array();
- /*
- * Игнорировать "SESSION ID"
- *
- * @access public
- * @var bool
- */
- var $ignore_sid = false;
- /*
- * Не кэшировать POST-запросы
- *
- * @access public
- * @var bool
- */
- var $post_no_cache = true;
- /*
- * Отправлять заголовок X-Forwarded-For
- *
- * @access public
- * @var bool
- */
- var $forwarded = true;
- /*
- * Заменить хост в отправляемых заголовках HTTP_REFERER
- *
- * @access public
- * @var bool
- */
- var $replace_ref = true;
- /*
- * Сокет
- *
- * @access private
- * @var resource
- */
- var $socket = null;
- /*
- * Кэш
- *
- * @access private
- * @var resource
- */
- var $cache = null;
- /*
- * Файл кэширования
- *
- * @access private
- * @var string
- */
- var $cache_file = null;
- /*
- * Аргументы URI
- *
- * @access private
- * @var string
- */
- var $uri_argument = null;
- /*
- * HTTP-запрос
- *
- * @access private
- * @var string
- */
- var $request = null;
- /*
- * Массив замены элементов в заголовках
- *
- * @access public
- * @var array
- */
- var $head_replace = array();
- /*
- * Массив удаления элементов из заголовков
- *
- * @access public
- * @var array
- */
- var $head_cut = array();
- /*
- * Массив замены элементов в теле страницы
- *
- * @access public
- * @var array
- */
- var $body_replace = array();
- /*
- * Массив удаления элементов из тела страницы
- *
- * @access public
- * @var array
- */
- var $body_cut = array();
- /*
- * Тип мета-данных
- *
- * @access private
- * @var string
- */
- var $ret_type = null;
- /*
- * Контент заголовков
- *
- * @access private
- * @var string
- */
- var $head = null;
- /*
- * Контент тела страницы
- *
- * @access private
- * @var string
- */
- var $body = null;
- /*
- * Обозначение создания SSL cоединения с сервером
- *
- * @access private
- * @var string
- */
- var $http_ssl = false;
- /*
- * Тип SSL соединения с сервером
- *
- * @access private
- * @var string
- */
- var $http_ssl_type = STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
- /*
- * Кодировка страницы
- *
- * @access private
- * @var string
- */
- var $charset = 'UTF-8';
- /**
- * Запуск класса
- *
- * @access private
- */
- function run_cache()
- {
- ob_start();
- $this->get_connect();
- if (!$this->uri_argument = $this->prepare_uri())
- {
- return false;
- }
- if ($this->check_forbidden())
- {
- $this->print_error('Not access!', 'You don\'t have permission to access '.$this->uri_argument.' resource.');
- }
- $this->cache_file = $this->get_file();
- $this->request = $this->get_request();
- if ($this->check_exclude())
- {
- $this->no_cache();
- }
- else
- {
- if (file_exists($this->cache_file))
- {
- if ((time() - $this->second_to_hour($this->cache_time)) < filemtime($this->cache_file))
- {
- $this->read_cache();
- }
- else
- {
- $this->create_cache();
- }
- }
- else
- {
- $this->create_cache();
- }
- }
- $headers = $this->headers_parse($this->head);
- $this->ret_type = $headers['Content-Type'];
- $this->charset = $this->charset_page();
- if ($this->is_text())
- {
- if (isset($headers['Content-Encoding']) && preg_match('#gzip#i', $headers['Content-Encoding']) && function_exists('gzinflate'))
- {
- $this->body = $this->gz_decode($this->body);
- }
- $this->print_headers($this->header_wrapper($this->head));
- $this->print_body($this->body_wrapper($this->body));
- }
- }
- /**
- * Проверка на исключения и индивидуальное кэширование
- *
- * @return bool
- * @access private
- */
- function check_exclude()
- {
- if ($this->post_no_cache)
- {
- if ($_SERVER['REQUEST_METHOD'] == 'POST')
- {
- return true;
- }
- }
- foreach ($this->custom_cache as $key => $val)
- {
- if (preg_match('#'.str_replace('#', '\\#', $key).'#i', $this->uri_argument))
- {
- if ($val)
- {
- $this->cache_time = $val;
- return false;
- }
- return true;
- }
- }
- }
- /**
- * Проверка на запрет доступа
- *
- * @return bool
- * @access private
- */
- function check_forbidden()
- {
- foreach ($this->forbidden as $key => $val)
- {
- if (preg_match('#'.str_replace('#', '\\#', $val).'#i', $this->uri_argument))
- {
- return true;
- }
- }
- return false;
- }
- /**
- * Перевод часов в секунды
- *
- * @param string $second
- * @return string
- * @access private
- */
- function second_to_hour($second)
- {
- return $second * 3600;
- }
- /**
- * Сформировать HTTP-запрос
- *
- * @return string
- * @access private
- */
- function get_request()
- {
- $method = $_SERVER['REQUEST_METHOD'];
- $headers = $this->get_user_headers();
- $post = $this->get_user_post($headers);
- $request = $method.' '.(($this->proxy_connect !== null) ? $this->scheme.'://'.$this->host : '').$this->uri_argument." HTTP/1.0\r\n";
- $headers['Host'] = $this->host;
- if ($this->forwarded)
- {
- $headers['X-Forwarded-For'] = $_SERVER['REMOTE_ADDR'];
- }
- if (isset($headers['Referer']) && $this->replace_ref)
- {
- $referer = parse_url($headers['Referer']);
- $headers['Referer'] = $referer['scheme'].'://'.$this->host.$referer['path'].(isset($referer['query']) ? '?'.$referer['query'] : '');
- }
- if ($this->proxy_user && $this->proxy_pass)
- {
- $headers['Proxy-Authorization'] = 'basic '.base64_encode($this->proxy_user.':'.$this->proxy_pass);
- }
- unset($headers['Accept-Encoding']);
- $headers['Connection'] = 'close';
- if ($method == 'POST')
- {
- $headers['Content-Length'] = strlen($post);
- $request .= $this->headers_build($headers)."\r\n";
- $request .= $post;
- }
- else
- {
- $request .= $this->headers_build($headers)."\r\n";
- }
- return $request;
- }
- /**
- * Получить отправленные POST-значения
- *
- * @return string
- * @access private
- */
- function get_user_post($headers)
- {
- if (empty($HTTP_RAW_POST_DATA))
- {
- if (strlen(file_get_contents("php://input")) > 0)
- {
- return file_get_contents("php://input");
- }
- return $this->multipart_user_post($headers);
- }
- else
- {
- return $HTTP_RAW_POST_DATA;
- }
- }
- /**
- * Собрать отправленные POST-значения (multipart/form-data)
- *
- * @return string
- * @access private
- */
- function multipart_user_post($headers)
- {
- $boundary = preg_replace("|multipart\/form-data; boundary\=(.*)|is", "\\1", $headers['Content-Type']);
- if (sizeof($_FILES) > 0 and strlen($boundary))
- {
- $data = '';
- $boundary = '--'.$boundary;
- foreach ($_FILES as $key => $file_data)
- {
- $data .= $boundary."\r\n";
- $data .= 'Content-Disposition: form-data; name="'.$key.'"; filename="'.$file_data['name'].'"'."\r\n";
- $data .= 'Content-Type: '.$file_data['type']."\r\n\r\n";
- $data .= file_get_contents($file_data['tmp_name'])."\r\n";
- }
- foreach ($_POST as $key => $value)
- {
- $data .= $boundary."\r\n";
- $data .= 'Content-Disposition: form-data; name="'.$key.'" '."\r\n\r\n";
- $data .= $value."\r\n";
- }
- $data .= $boundary.'--';
- $post = 'Content-Type: '.$headers['Content-Type']."\r\n";
- $post .= 'Content-Length: '.strlen($data)."\r\n\r\n";
- $post .= $data;
- return $post;
- }
- return false;
- }
- /**
- * Получить отправленное значение URI
- *
- * @return string
- * @access private
- */
- function get_user_uri()
- {
- if ($uri = $_SERVER['REQUEST_URI'])
- {
- return $uri;
- }
- else
- {
- return '/';
- }
- }
- /**
- * Получить отправленные заголовки HTTP
- *
- * @return string
- * @access private
- */
- function get_user_headers()
- {
- if (!function_exists('getallheaders'))
- {
- $headers = array();
- foreach ($_SERVER as $name => $value)
- {
- if (substr($name, 0, 5) == 'HTTP_')
- {
- $name = strtolower(str_replace('_', ' ', substr($name, 5)));
- $name = str_replace(' ', '-', ucwords($name));
- $headers[$name] = $value;
- }
- }
- return $headers;
- }
- else
- {
- return getallheaders();
- }
- }
- /**
- * Создать массив элементов URI
- *
- * @return array
- * @access public
- */
- function uri_array()
- {
- return explode('/', $this->get_user_uri());
- }
- /**
- * Получить и подготовить URI
- *
- * @return mixed
- * @access private
- */
- function prepare_uri()
- {
- $uri = $this->get_user_uri();
- $path = $this->prepare_path();
- if (!preg_match('#^/'.str_replace('#', '\\#', $path).'#', $uri))
- {
- return false;
- }
- if ($path)
- {
- $uri = preg_replace('|^/'.str_replace('|', '\\|', $path).'|isU', '', $uri);
- }
- if ($this->ignore_sid)
- {
- $uri = $this->replace_sid($uri);
- }
- return $uri;
- }
- /**
- * Подготовить путевое значение
- *
- * @return string
- * @access private
- */
- function prepare_path()
- {
- return trim($this->path, '/');
- }
- /**
- * Получить имя хоста и порт для соединения
- */
- function get_connect()
- {
- $connect = parse_url($this->connect);
- $proxy_connect = parse_url($this->proxy_connect);
- $this->host = $connect['host'];
- $this->port = isset($connect['port']) ? $connect['port'] : ($connect['scheme'] == 'https' ? 443 : 80);
- $this->scheme = $connect['scheme'];
- if (isset($proxy_connect['host']) && isset($proxy_connect['port']))
- {
- $this->proxy_host = $proxy_connect['host'];
- $this->proxy_port = isset($proxy_connect['port']) ? $proxy_connect['port'] : 80;
- }
- }
- /**
- * Вывести заголовки
- *
- * @param string $raw
- * @access private
- */
- function print_headers($raw)
- {
- $array = $this->headers_parse($raw);
- $user_headers = $this->get_user_headers();
- if (isset($array['ETag']) && strlen($array['ETag']) > 0 && isset($user_headers['If-None-Match']) && isset($array['ETag']) && ($user_headers['If-None-Match'] == $array['ETag']))
- {
- header('HTTP/1.0 304 Not Modified');
- die;
- }
- else
- {
- if ($raw_array = explode("\n", $raw))
- {
- header('HTTP/1.0 '.trim(substr($raw_array[0], 9, strlen($raw_array[0]))));
- }
- }
- $array['X-Mirrored-By'] = 'HSDN Mirroring Cacher'.(defined('MCP_VERSION') ? '/'.MCP_VERSION : null);
- foreach ($array as $key => $value)
- {
- if ($key && $value && $key != 'Transfer-Encoding')
- {
- header($key.': '.$value);
- }
- }
- ob_end_flush();
- }
- /**
- * Вывести тело страницы
- *
- * @param string $body
- * @access private
- */
- function print_body($body)
- {
- print $body;
- flush();
- }
- /**
- * Не кэшировать контент
- *
- * @access private
- */
- function no_cache()
- {
- $this->delete_file();
- $this->socket = $this->create_socket();
- $this->write_socket($this->request);
- $this->head = $this->get_head($this->socket);
- $headers = $this->headers_parse($this->head);
- $this->ret_type = $headers['Content-Type'];
- $this->body = $this->get_body($this->socket);
- $this->close_socket();
- }
- /**
- * Создать кэш
- *
- * @access private
- */
- function create_cache()
- {
- $this->socket = $this->create_socket();
- $this->write_socket($this->request);
- $this->head = $this->get_head($this->socket);
- $headers = $this->headers_parse($this->head);
- $this->ret_type = $headers['Content-Type'];
- if (file_exists($this->cache_file))
- {
- if (isset($headers['ETag']))
- {
- $this->read_cache();
- $cache_headers = $this->headers_parse($this->head);
- if (file_exists($this->cache_file) && ($headers['ETag'] == $cache_headers['ETag']))
- {
- $body = $this->body;
- }
- else
- {
- $body = $this->get_body($this->socket);
- $this->write_cache($this->head, $body);
- }
- }
- if (!isset($headers['Content-Length']))
- {
- $headers['Content-Length'] = 0;
- }
- if (filesize($this->cache_file) != $headers['Content-Length']) // Ошибка?
- {
- if (!strlen($body))
- {
- $body = $this->get_body($this->socket);
- }
- $this->write_cache($this->head, $body);
- }
- }
- else
- {
- $body = $this->get_body($this->socket);
- $this->write_cache($this->head, $body);
- }
- $this->close_socket();
- $this->body = $body;
- }
- /**
- * Записать в кэш
- *
- * @param string $head
- * @param string $body
- * @access private
- */
- function write_cache($head, $body)
- {
- if ($this->httpcode_parse($head) == 200 && trim($head) && trim($body))
- {
- $this->cache = $this->create_file();
- $this->write_file($head."\r\n".$body);
- $this->close_file();
- }
- }
- /**
- * Прочитать кэш
- *
- * @access private
- */
- function read_cache()
- {
- $this->cache = $this->open_file();
- $this->head = $this->get_head($this->cache);
- $headers = $this->headers_parse($this->head);
- $this->ret_type = $headers['Content-Type'];
- $this->body = $this->get_body($this->cache);
- $this->close_file();
- }
- /**
- * Подключиться к серверу и открыть сокет
- *
- * @return resource
- * @access private
- */
- function create_socket()
- {
- if ($this->proxy_connect)
- {
- $fp = @fsockopen($this->proxy_host, $this->proxy_port, $errno, $errstr, 10);
- }
- else
- {
- $fp = @fsockopen($this->host, $this->port, $errno, $errstr, 10);
- }
- if (!is_resource($fp))
- {
- $this->print_error('Bad Gateway!', $errstr.'.');
- }
- if ($this->http_ssl and
- function_exists('stream_socket_enable_crypto') and
- function_exists('stream_get_transports') and
- in_array('ssl', stream_get_transports()))
- {
- stream_socket_enable_crypto($fp, TRUE, $this->http_ssl_type);
- }
- return $fp;
- }
- /**
- * Записать данные в сокет
- *
- * @param string $request
- * @access private
- */
- function write_socket($request)
- {
- if (!fputs($this->socket, $request))
- {
- $this->print_error('Remote Error!', 'Can\'t send request to server.');
- }
- }
- /**
- * Закрыть сокет
- *
- * @access private
- */
- function close_socket()
- {
- if (is_resource($this->socket))
- {
- fclose($this->socket);
- }
- $this->socket = null;
- }
- /**
- * Получить имя файла хранения кэша
- *
- * @return string
- * @access private
- */
- function get_file()
- {
- return $this->cache_dir.'/'.md5($this->uri_argument);
- }
- /**
- * Открыть файл хранения кэша
- *
- * @return resource
- * @access private
- */
- function open_file()
- {
- if (!$cache = @fopen($this->cache_file, 'r'))
- {
- $this->print_error('Internal Error!', 'Can\'t read '.$this->cache_file.' Cache File.');
- }
- return $cache;
- }
- /**
- * Закрыть кэш-файла
- *
- * @access private
- */
- function close_file()
- {
- if (is_resource($this->cache))
- {
- fclose($this->cache);
- }
- $this->cache = null;
- }
- /**
- * Создать файл хранения кэша
- *
- * @return resource
- * @access private
- */
- function create_file()
- {
- $this->create_dir($this->cache_dir);
- if (!$cache = @fopen($this->cache_file, 'w'))
- {
- $this->print_error('Internal Error!', 'Can\'t open '.$this->cache_file.' Cache File for write.');
- }
- return $cache;
- }
- /**
- * Создать директории для хранения кэша (в том числе рекурсивно)
- *
- * @param string $path
- * @param int $mode
- * @return bool
- * @access private
- */
- function create_dir($path, $mode = 0777)
- {
- if (file_exists($path))
- {
- return;
- }
- $dirs = explode(DIRECTORY_SEPARATOR, $path);
- $count = count($dirs);
- $path = '';
- for ($i = 0; $i < $count; ++$i)
- {
- $path .= $dirs[$i].DIRECTORY_SEPARATOR;
- if (!@is_dir($path))
- {
- @mkdir($path, $mode);
- }
- }
- }
- /**
- * Удалить файл хранения кэша
- *
- * @access private
- */
- function delete_file()
- {
- if (file_exists($this->cache))
- {
- if (!@unlink($this->cache))
- {
- $this->print_error('Internal Error!', 'Can\'t delete '.$this->cache_file.' Cache File.');
- }
- }
- }
- /**
- * Записать данные в файл хранения кэша
- *
- * @param string $source
- * @access private
- */
- function write_file($source)
- {
- if (!fwrite($this->cache, $source))
- {
- $this->print_error('Internal Error!', 'Can\'t write '.$this->cache_file.' Cache File.');
- }
- }
- /**
- * Прочитать заголовки из сокета
- *
- * @param resource $handler
- * @return string
- * @access private
- */
- function get_head($handler)
- {
- $head = '';
- while (!feof($handler))
- {
- $line = fgets($handler, 2048);
- if ($line == "\r\n" || $line == "\n")
- {
- break;
- }
- $head .= $line;
- }
- return $head;
- }
- /**
- * Прочитать тело из сокета
- *
- * @param resource $handler
- * @return string
- * @access private
- */
- function get_body($handler)
- {
- $body = '';
- if (!$this->is_text())
- {
- $this->print_headers($this->header_wrapper($this->head));
- }
- while (!feof($handler))
- {
- $body .= $line = fread($handler, 4096);
- if (!$this->is_text())
- {
- $this->print_body($line);
- }
- }
- return $body;
- }
- /**
- * Проверить тип контента на текстовый
- *
- * @return bool
- * @access private
- */
- function is_text()
- {
- if (preg_match('#(text|xhtml|xml)#', $this->ret_type))
- {
- return true;
- }
- return false;
- }
- /**
- * Получить кодировку полученной страницы
- *
- * @return string
- * @access private
- */
- function charset_page()
- {
- if (@preg_match("/charset=(.*)/", $this->ret_type, $charset))
- {
- return $charset[1];
- }
- if (@preg_match("/charset=(.*)(\'|\")/", $this->body, $charset))
- {
- return $charset[1];
- }
- return $this->charset;
- }
- /**
- * Получить HTTP-код из заголовков
- *
- * @param string $head
- * @return string
- * @access private
- */
- function httpcode_parse($head)
- {
- if (strlen($head) < 12)
- {
- return FALSE;
- }
- $status = substr($head, 9, 3);
- if (!is_numeric($status))
- {
- return FALSE;
- }
- return $status;
- }
- /**
- * Создать массив заголовков HTTP
- *
- * @param string $headers
- * @return mixed
- * @access private
- */
- function headers_parse($headers)
- {
- $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $headers));
- $headers = array();
- if (sizeof($lines) > 0)
- {
- foreach ($lines as $line)
- {
- $header = explode(': ', $line);
- if (isset($header[0]) AND isset($header[1]))
- {
- $headers[$header[0]] = trim($header[1]);
- }
- }
- }
- return $headers;
- }
- /**
- * Собрать HTTP-заголовки из массива
- *
- * @param array $headers
- * @return string
- * @access private
- */
- function headers_build($headers)
- {
- if (sizeof($headers) == 0)
- {
- return '';
- }
- $headers_string = '';
- foreach ($headers as $name => $value)
- {
- if ($name != '' AND $value != '')
- {
- $headers_string .= $name.': '.$value."\r\n";
- }
- }
- return $headers_string."\r\n";
- }
- /**
- * Вывести сообщение об ошибки и завершить работу класса
- *
- * @param string $error
- * @param string $subject
- * @access private
- */
- function print_error($error, $subject)
- {
- header('HTTP/1.0 500 Internal Server Error');
- $for = ($this->host) ? ' for <a href="http://'.$this->host.'">'.$this->host.'</a>' : null;
- $ver = defined('MCP_VERSION') ? '/'.MCP_VERSION : null;
- print '<html><head></head><body bgcolor="#CCCCFF"><h1>'.$error.'</h1>'.
- $subject.'<p><hr><address>HSDN Mirroring Cacher'.$ver.
- $for.'</address></body></html>';
- exit();
- }
- /**
- * Обработать заголовки
- *
- * @param string $raw
- * @return string
- * @access private
- */
- function header_wrapper($raw)
- {
- $raw = $this->replace($this->head_replace, $raw);
- $raw = $this->cut($this->head_cut, $raw);
- return $raw;
- }
- /**
- * Обработать тело страницы
- *
- * @param string $raw
- * @return string
- * @access private
- */
- function body_wrapper($raw)
- {
- $raw = $this->replace($this->body_replace, $raw);
- $raw = $this->cut($this->body_cut, $raw);
- return $raw;
- }
- /**
- * Удалить "SESSION ID" из URI
- *
- * @param string $raw
- * @return string
- * @access private
- */
- function replace_sid($url)
- {
- $ret = preg_replace('/([0-9A-Za-z]+)\=([0-9a-f]{32})/', '', $url);
- if ($ret != $url)
- {
- $ret = preg_replace('/\?\&/', '?', $ret);
- $ret = preg_replace('/((\&|\?){1})$/', '', $ret);
- }
- return $ret;
- }
- /**
- * Заменить фрагменты в контенте
- *
- * @param array $array
- * @param string $source
- * @return string
- * @access private
- */
- function replace($array, $source)
- {
- foreach ($array as $key => $val)
- {
- $source = preg_replace('|'.str_replace('|', '\\|', $key).'|sU', $val, $source);
- }
- return $source;
- }
- /**
- * Удалить фрагменты из контента
- *
- * @param array $array
- * @param string $source
- * @return string
- * @access private
- */
- function cut($array, $source)
- {
- foreach ($array as $val)
- {
- preg_match('|'.str_replace('|', '\\|', $val).'|isU', $source, $rep);
- if (isset($rep[0]))
- {
- $source = str_replace($rep[0], '', $source);
- }
- }
- return $source;
- }
- /**
- * Декодировать сжатый GZip-контент
- *
- * @param string $data
- * @return string
- * @access private
- */
- function gz_decode($data)
- {
- $flags = ord(substr($data, 3, 1));
- $headerlen = 10;
- $extralen = 0;
- $filenamelen = 0;
- if ($flags & 4)
- {
- $extralen = unpack('v' ,substr($data, 10, 2));
- $extralen = $extralen[1];
- $headerlen += 2 + $extralen;
- }
- if ($flags & 8)
- {
- $headerlen = strpos($data, chr(0), $headerlen) + 1;
- }
- if ($flags & 16)
- {
- $headerlen = strpos($data, chr(0), $headerlen) + 1;
- }
- if ($flags & 2)
- {
- $headerlen += 2;
- }
- $unpacked = gzinflate(substr($data, $headerlen));
- if ($unpacked === false)
- {
- $unpacked = $data;
- }
- return $unpacked;
- }
- }
- // End
|