AnonSec Shell
Server IP : 85.193.89.191  /  Your IP : 3.15.144.56
Web Server : Apache
System : Linux 956367-cx40159.tmweb.ru 3.10.0-1160.105.1.el7.x86_64 #1 SMP Thu Dec 7 15:39:45 UTC 2023 x86_64
User : bitrix ( 600)
PHP Version : 8.1.27
Disable Function : NONE
MySQL : OFF  |  cURL : OFF  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /home/bitrix/www/assets/err_class/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ HOME ]     

Current File : /home/bitrix/www/assets/err_class/sape.php
<?php
/**
 * SAPE.ru - Интеллектуальная система купли-продажи ссылок
 *
 * PHP-клиент
 *
 * Вебмастеры! Не нужно ничего менять в этом файле!
 * Все настройки - через параметры при вызове кода.
 *
 * Подробную информацию по добавлению сайта в систему,
 * установки кода, а так же по всему остальным вопросам
 * Вы можете найти здесь:
 * @link http://help.sape.ru/sape/faq/27
 * @link http://help.sape.ru/articles/faq/1041
 *
 */

/**
 * Основной класс, выполняющий всю рутину
 */
class SAPE_base
{
    protected $_version = '1.4.9';

    protected $_verbose = true;

    /**
     * Кодировка сайта
     * @link http://www.php.net/manual/en/function.iconv.php
     * @var string
     */
    protected $_charset = '';

    protected $_sape_charset = '';

    protected $_server_list = array('dispenser-01.saperu.net', 'dispenser-02.saperu.net');

    /**
     * Пожалейте наш сервер :о)
     * @var int
     */
    protected $_cache_lifetime = 3600;

    /**
     * Если скачать базу ссылок не удалось, то следующая попытка будет через столько секунд
     * @var int
     */
    protected $_cache_reloadtime = 600;

    protected $_errors = array();

    protected $_host = '';

    protected $_request_uri = '';

    protected $_multi_site = false;

    /**
     * Способ подключения к удалённому серверу [file_get_contents|curl|socket]
     * @var string
     */
    protected $_fetch_remote_type = 'socket';

    /**
     * Сколько ждать ответа
     * @var int
     */
    protected $_socket_timeout = 6;

    protected $_force_show_code = true;

    /**
     * Если наш робот
     * @var bool
     */
    protected $_is_our_bot = false;

    protected $_debug                   = false;
    protected $_file_contents_for_debug = array();

    /**
     * Регистронезависимый режим работы, использовать только на свой страх и риск
     * @var bool
     */
    protected $_ignore_case = false;

    /**
     * Путь к файлу с данными
     * @var string
     */
    protected $_db_file = '';

    /**
     * Формат запроса. serialize|php-require
     * @var string
     */
    protected $_format = 'serialize';

    /**
     * Флаг для разбиения links.db по отдельным файлам.
     * @var bool
     */
    protected $_split_data_file = false;
    /**
     * Откуда будем брать uri страницы: $_SERVER['REQUEST_URI'] или getenv('REQUEST_URI')
     * @var bool
     */
    protected $_use_server_array = false;

    /**
     * Показывать ли код js отдельно от выводимого контента
     *
     * @var bool
     */
    protected $_show_counter_separately = false;

    protected $_force_update_db = false;

    protected $_user_agent = '';

    public function __construct($options = null)
    {

        // Поехали :o)

        $host = '';

        if (is_array($options)) {
            if (isset($options['host'])) {
                $host = $options['host'];
            }
        } elseif (strlen($options)) {
            $host    = $options;
            $options = array();
        } else {
            $options = array();
        }

        if (isset($options['use_server_array']) && $options['use_server_array'] == true) {
            $this->_use_server_array = true;
        }

        // Какой сайт?
        if (strlen($host)) {
            $this->_host = $host;
        } else {
            $this->_host = $_SERVER['HTTP_HOST'];
        }

        $this->_host = preg_replace('/^https:\/\//', '', $this->_host);
        $this->_host = preg_replace('/^www\./', '', $this->_host);

        // Какая страница?
        if (isset($options['request_uri']) && strlen($options['request_uri'])) {
            $this->_request_uri = $options['request_uri'];
        } elseif ($this->_use_server_array === false) {
            $this->_request_uri = getenv('REQUEST_URI');
        }

        if (strlen($this->_request_uri) == 0) {
            $this->_request_uri = $_SERVER['REQUEST_URI'];
        }

        // На случай, если хочется много сайтов в одной папке
        if (isset($options['multi_site']) && $options['multi_site'] == true) {
            $this->_multi_site = true;
        }

        // Выводить информацию о дебаге
        if (isset($options['debug']) && $options['debug'] == true) {
            $this->_debug = true;
        }

        // Определяем наш ли робот
        if (isset($_COOKIE['sape_cookie']) && ($_COOKIE['sape_cookie'] == _SAPE_USER)) {
            $this->_is_our_bot = true;
            if (isset($_COOKIE['sape_debug']) && ($_COOKIE['sape_debug'] == 1)) {
                $this->_debug = true;
                //для удобства дебега саппортом
                $this->_options            = $options;
                $this->_server_request_uri = $_SERVER['REQUEST_URI'];
                $this->_getenv_request_uri = getenv('REQUEST_URI');
                $this->_SAPE_USER          = _SAPE_USER;
            }
            if (isset($_COOKIE['sape_updatedb']) && ($_COOKIE['sape_updatedb'] == 1)) {
                $this->_force_update_db = true;
            }
        } else {
            $this->_is_our_bot = false;
        }

        // Сообщать об ошибках
        if (isset($options['verbose']) && $options['verbose'] == true || $this->_debug) {
            $this->_verbose = true;
        }

        // Кодировка
        if (isset($options['charset']) && strlen($options['charset'])) {
            $this->_charset = $options['charset'];
        } else {
            $this->_charset = 'windows-1251';
        }

        if (isset($options['fetch_remote_type']) && strlen($options['fetch_remote_type'])) {
            $this->_fetch_remote_type = $options['fetch_remote_type'];
        }

        if (isset($options['socket_timeout']) && is_numeric($options['socket_timeout']) && $options['socket_timeout'] > 0) {
            $this->_socket_timeout = $options['socket_timeout'];
        }

        // Всегда выводить чек-код
        if (isset($options['force_show_code']) && $options['force_show_code'] == true) {
            $this->_force_show_code = true;
        }

        if (!defined('_SAPE_USER')) {
            return $this->_raise_error('Не задана константа _SAPE_USER');
        }

        //Не обращаем внимания на регистр ссылок
        if (isset($options['ignore_case']) && $options['ignore_case'] == true) {
            $this->_ignore_case = true;
            $this->_request_uri = strtolower($this->_request_uri);
        }

        if (isset($options['show_counter_separately'])) {
            $this->_show_counter_separately = (bool)$options['show_counter_separately'];
        }

        if (isset($options['format']) && in_array($options['format'], array('serialize', 'php-require'))) {
            $this->_format = $options['format'];
        }

        if (isset($options['split_data_file'])) {
            $this->_split_data_file = (bool)$options['split_data_file'];
        }
    }

    /**
     * Получить строку User-Agent
     *
     * @return string
     */
    protected function _get_full_user_agent_string()
    {
        return $this->_user_agent . ' ' . $this->_version;
    }

    /**
     * Вывести дебаг-информацию
     *
     * @param $data
     *
     * @return string
     */
    protected function _debug_output($data)
    {
        $data = '<!-- <sape_debug_info>' . @base64_encode(serialize($data)) . '</sape_debug_info> -->';

        return $data;
    }

    /**
     * Функция для подключения к удалённому серверу
     */
    protected function _fetch_remote_file($host, $path, $specifyCharset = false)
    {

        $user_agent = $this->_get_full_user_agent_string();

        @ini_set('allow_url_fopen', 1);
        @ini_set('default_socket_timeout', $this->_socket_timeout);
        @ini_set('user_agent', $user_agent);
        if (
            $this->_fetch_remote_type == 'file_get_contents'
            ||
            (
                $this->_fetch_remote_type == ''
                &&
                function_exists('file_get_contents')
                &&
                ini_get('allow_url_fopen') == 1
            )
        ) {
            $this->_fetch_remote_type = 'file_get_contents';

            if ($specifyCharset && function_exists('stream_context_create')) {
                $opts    = array(
                    'http' => array(
                        'method' => 'GET',
                        'header' => 'Accept-Charset: ' . $this->_charset . "
"
                    )
                );
                $context = @stream_context_create($opts);
                if ($data = @file_get_contents('http://' . $host . $path, null, $context)) {
                    return $data;
                }
            } else {
                if ($data = @file_get_contents('http://' . $host . $path)) {
                    return $data;
                }
            }
        } elseif (
            $this->_fetch_remote_type == 'curl'
            ||
            (
                $this->_fetch_remote_type == ''
                &&
                function_exists('curl_init')
            )
        ) {
            $this->_fetch_remote_type = 'curl';
            if ($ch = @curl_init()) {

                @curl_setopt($ch, CURLOPT_URL, 'http://' . $host . $path);
                @curl_setopt($ch, CURLOPT_HEADER, false);
                @curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                @curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->_socket_timeout);
                @curl_setopt($ch, CURLOPT_USERAGENT, $user_agent);
                if ($specifyCharset) {
                    @curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept-Charset: ' . $this->_charset));
                }

                $data = @curl_exec($ch);
                @curl_close($ch);

                if ($data) {
                    return $data;
                }
            }
        } else {
            $this->_fetch_remote_type = 'socket';
            $buff                     = '';
            $fp                       = @fsockopen($host, 80, $errno, $errstr, $this->_socket_timeout);
            if ($fp) {
                @fputs($fp, "GET {$path} HTTP/1.0
Host: {$host}
");
                if ($specifyCharset) {
                    @fputs($fp, "Accept-Charset: {$this->_charset}
");
                }
                @fputs($fp, "User-Agent: {$user_agent}

");
                while (!@feof($fp)) {
                    $buff .= @fgets($fp, 128);
                }
                @fclose($fp);

                $page = explode("

", $buff);
                unset($page[0]);

                return implode("

", $page);
            }
        }

        return $this->_raise_error('Не могу подключиться к серверу: ' . $host . $path . ', type: ' . $this->_fetch_remote_type);
    }

    /**
     * Функция чтения из локального файла
     */
    protected function _read($filename)
    {

        $fp = @fopen($filename, 'rb');
        @flock($fp, LOCK_SH);
        if ($fp) {
            clearstatcache();
            $length = @filesize($filename);

            if (version_compare(PHP_VERSION, '5.3.0', '<')) {
                $mqr = @get_magic_quotes_runtime();
                @set_magic_quotes_runtime(0);
            }

            if ($length) {
                $data = @fread($fp, $length);
            } else {
                $data = '';
            }

            if (version_compare(PHP_VERSION, '5.3.0', '<')) {
                @set_magic_quotes_runtime($mqr);
            }

            @flock($fp, LOCK_UN);
            @fclose($fp);

            return $data;
        }

        return $this->_raise_error('Не могу считать данные из файла: ' . $filename);
    }

    /**
     * Функция записи в локальный файл
     */
    protected function _write($filename, $data)
    {

        $fp = @fopen($filename, 'ab');
        if ($fp) {
            if (flock($fp, LOCK_EX | LOCK_NB)) {
                ftruncate($fp, 0);

                if (version_compare(PHP_VERSION, '5.3.0', '<')) {
                    $mqr = @get_magic_quotes_runtime();
                    @set_magic_quotes_runtime(0);
                }

                @fwrite($fp, $data);

                if (version_compare(PHP_VERSION, '5.3.0', '<')) {
                    @set_magic_quotes_runtime($mqr);
                }

                @flock($fp, LOCK_UN);
                @fclose($fp);

                if (md5($this->_read($filename)) != md5($data)) {
                    @unlink($filename);

                    return $this->_raise_error('Нарушена целостность данных при записи в файл: ' . $filename);
                }
            } else {
                return false;
            }

            return true;
        }

        return $this->_raise_error('Не могу записать данные в файл: ' . $filename);
    }

    /**
     * Функция обработки ошибок
     */
    protected function _raise_error($e)
    {

        $this->_errors[] = $e;

        if ($this->_verbose == true) {
            print '<p style="color: red; font-weight: bold;">SAPE ERROR: ' . $e . '</p>';
        }

        return false;
    }

    /**
     * Получить имя файла с даными
     *
     * @return string
     */
    protected function _get_db_file()
    {
        return '';
    }

    /**
     * Получить имя файла с мета-информацией
     *
     * @return string
     */
    protected function _get_meta_file()
    {
        return '';
    }

    /**
     * Получить префикс файла в режиме split_data_file.
     *
     * @return string
     */
    protected function _get_save_filename_prefix()
    {
        if ($this->_split_data_file) {
            return '.' . crc32($this->_request_uri) % 100;
        } else {
            return '';
        }
    }
    /**
     * Получить URI к хосту диспенсера
     *
     * @return string
     */
    protected function _get_dispenser_path()
    {
        return '';
    }

    /**
     * Сохранить данные, полученные из файла, в объекте
     */
    protected function _set_data($data)
    {
    }

    /**
     * Расшифровывает данные
     *
     * @param string $data
     *
     * @return array|bool
     */
    protected function _uncode_data($data)
    {
        return @unserialize($data);
    }

    /**
     * Шифрует данные для сохранения.
     *
     * @param $data
     *
     * @return string
     */
    protected function _code_data($data)
    {
        return @serialize($data);
    }

    /**
     * Сохранение данных в файл.
     *
     * @param string $data
     * @param string $filename
     */
    protected function _save_data($data, $filename = '')
    {
        $this->_write($filename, $data);
    }
    /**
     * Загрузка данных
     */
    protected function _load_data()
    {
        $this->_db_file = $this->_get_db_file();

        if (!is_file($this->_db_file)) {
            // Пытаемся создать файл.
            if (@touch($this->_db_file, time() - $this->_cache_lifetime - 1)) {
                @chmod($this->_db_file, 0666); // Права доступа
            } else {
                return $this->_raise_error('Нет файла ' . $this->_db_file . '. Создать не удалось. Выставите права 777 на папку.');
            }
        }

        if (!is_writable($this->_db_file)) {
            return $this->_raise_error('Нет доступа на запись к файлу: ' . $this->_db_file . '! Выставите права 777 на папку.');
        }

        @clearstatcache();

        $data = $this->_read($this->_db_file);
        if (
            $this->_force_update_db
            || (
                !$this->_is_our_bot
                &&
                (
                    filemtime($this->_db_file) < (time() - $this->_cache_lifetime)
                )
            )
        ) {
            // Чтобы не повесить площадку клиента и чтобы не было одновременных запросов
            @touch($this->_db_file, (time() - $this->_cache_lifetime + $this->_cache_reloadtime));

            $path = $this->_get_dispenser_path();
            if (strlen($this->_charset)) {
                $path .= '&charset=' . $this->_charset;
            }
            if ($this->_format) {
                $path .= '&format=' . $this->_format;
            }
            foreach ($this->_server_list as $server) {
                if ($data = $this->_fetch_remote_file($server, $path)) {
                    if (substr($data, 0, 12) == 'FATAL ERROR:') {
                        $this->_raise_error($data);
                    } else {
                        // [псевдо]проверка целостности:
                        $hash = $this->_uncode_data($data);
                        if ($hash != false) {
                            // попытаемся записать кодировку в кеш
                            $hash['__sape_charset__']      = $this->_charset;
                            $hash['__last_update__']       = time();
                            $hash['__multi_site__']        = $this->_multi_site;
                            $hash['__fetch_remote_type__'] = $this->_fetch_remote_type;
                            $hash['__ignore_case__']       = $this->_ignore_case;
                            $hash['__php_version__']       = phpversion();
                            $hash['__server_software__']   = $_SERVER['SERVER_SOFTWARE'];

                            $data_new = $this->_code_data($hash);
                            if ($data_new) {
                                $data = $data_new;
                            }

                            $this->_save_data($data, $this->_db_file);
                            break;
                        }
                    }
                }
            }
        }

        // Убиваем PHPSESSID
        if (strlen(session_id())) {
            $session            = session_name() . '=' . session_id();
            $this->_request_uri = str_replace(array('?' . $session, '&' . $session), '', $this->_request_uri);
        }
        $data = $this->_uncode_data($data);
        if ($this->_split_data_file) {
            $meta = $this->_uncode_data($this->_read($this->_get_meta_file()));
            if (!is_array($data)) {
                $data = array();
            }
            if (is_array($meta)) {
                $data = array_merge($data, $meta);
            }
        }
        $this->_set_data($data);

        return true;
    }

    protected function _return_obligatory_page_content()
    {
        $s_globals = new SAPE_globals();

        $html = '';
        if (isset($this->_page_obligatory_output) && !empty($this->_page_obligatory_output)
            && false == $s_globals->page_obligatory_output_shown()
        ) {
            $s_globals->page_obligatory_output_shown(true);
            $html = $this->_page_obligatory_output;
        }

        return $html;
    }

    /**
     * Вернуть js-код
     * - работает только когда параметр конструктора show_counter_separately = true
     *
     * @return string
     */
    public function return_counter()
    {
        //если show_counter_separately = false и выполнен вызов этого метода,
        //то заблокировать вывод js-кода вместе с контентом
        if (false == $this->_show_counter_separately) {
            $this->_show_counter_separately = true;
        }

        return $this->_return_obligatory_page_content();
    }
}

/**
 * Глобальные флаги
 */
class SAPE_globals
{

    protected function _get_toggle_flag($name, $toggle = false)
    {

        static $flags = array();

        if (!isset($flags[$name])) {
            $flags[$name] = false;
        }

        if ($toggle) {
            $flags[$name] = true;
        }

        return $flags[$name];
    }

    public function block_css_shown($toggle = false)
    {
        return $this->_get_toggle_flag('block_css_shown', $toggle);
    }

    public function block_ins_beforeall_shown($toggle = false)
    {
        return $this->_get_toggle_flag('block_ins_beforeall_shown', $toggle);
    }

    public function page_obligatory_output_shown($toggle = false)
    {
        return $this->_get_toggle_flag('page_obligatory_output_shown', $toggle);
    }
}

/**
 * Класс для работы с обычными ссылками
 */
class SAPE_client extends SAPE_base
{

    protected $_links_delimiter = '';
    protected $_links           = array();
    protected $_links_page      = array();
    protected $_teasers_page    = array();

    protected $_user_agent         = 'SAPE_Client PHP';
    protected $_show_only_block    = false;
    protected $_block_tpl          = '';
    protected $_block_tpl_options  = array();
    protected $_block_uri_idna     = array();
    protected $_return_links_calls;
    protected $_teasers_css_showed = false;

    /**
     * @var SAPE_rtb
     */
    protected $_teasers_rtb_proxy  = null;

    public function __construct($options = null)
    {
        parent::__construct($options);

        if (isset($options['rtb']) && !empty($options['rtb']) && $options['rtb'] instanceof SAPE_rtb) {
            $this->_teasers_rtb_proxy = $options['rtb'];
        }

        $this->_load_data();
    }

    /**
     * Обработка html для массива ссылок
     *
     * @param string     $html
     * @param null|array $options
     *
     * @return string
     */
    protected function _return_array_links_html($html, $options = null)
    {

        if (empty($options)) {
            $options = array();
        }

        // если запрошена определенная кодировка, и известна кодировка кеша, и они разные, конвертируем в заданную
        if (
            strlen($this->_charset) > 0
            &&
            strlen($this->_sape_charset) > 0
            &&
            $this->_sape_charset != $this->_charset
            &&
            function_exists('iconv')
        ) {
            $new_html = @iconv($this->_sape_charset, $this->_charset, $html);
            if ($new_html) {
                $html = $new_html;
            }
        }

        if ($this->_is_our_bot) {

            $html = '<sape_noindex>' . $html . '</sape_noindex>';

            if (isset($options['is_block_links']) && true == $options['is_block_links']) {

                if (!isset($options['nof_links_requested'])) {
                    $options['nof_links_requested'] = 0;
                }
                if (!isset($options['nof_links_displayed'])) {
                    $options['nof_links_displayed'] = 0;
                }
                if (!isset($options['nof_obligatory'])) {
                    $options['nof_obligatory'] = 0;
                }
                if (!isset($options['nof_conditional'])) {
                    $options['nof_conditional'] = 0;
                }

                $html = '<sape_block nof_req="' . $options['nof_links_requested'] .
                    '" nof_displ="' . $options['nof_links_displayed'] .
                    '" nof_oblig="' . $options['nof_obligatory'] .
                    '" nof_cond="' . $options['nof_conditional'] .
                    '">' . $html .
                    '</sape_block>';
            }
        }

        return $html;
    }

    /**
     * Финальная обработка html перед выводом ссылок
     *
     * @param string $html
     *
     * @return string
     */
    protected function _return_html($html)
    {
        if (false == $this->_show_counter_separately) {
            $html = $this->_return_obligatory_page_content() . $html;
        }

        return $this->_add_debug_info($html);
    }

    protected function _add_debug_info($html)
    {
        if ($this->_debug) {
            if (!empty($this->_links['__sape_teaser_images_path__'])) {
                $this->_add_file_content_for_debug($this->_links['__sape_teaser_images_path__']);
            }
            $this->_add_file_content_for_debug('.htaccess');

            $html .= $this->_debug_output($this);
        }

        return $html;
    }

    protected function _add_file_content_for_debug($file_name)
    {
        $path                                               = realpath(
            rtrim($_SERVER['DOCUMENT_ROOT'], DIRECTORY_SEPARATOR)
            . DIRECTORY_SEPARATOR
            . strtok($file_name, '?')
        );
        $this->_file_contents_for_debug[$file_name]['path'] = $path;
        if ($path) {
            $this->_file_contents_for_debug[$file_name]['contents'] = @file_get_contents($path);
        }
    }

    /**
     * Eсли запрошена определенная кодировка, и известна кодировка кеша, и они разные, конвертируем в заданную
     */
    protected function _convertCharset($html)
    {
        if (strlen($this->_charset) > 0
            && strlen($this->_sape_charset) > 0
            && $this->_sape_charset != $this->_charset
            && function_exists('iconv')
        ) {
            $new_html = @iconv($this->_sape_charset, $this->_charset, $html);
            if ($new_html) {
                $html = $new_html;
            }
        }

        return $html;
    }

    /**
     * Вывод ссылок в виде блока
     *
     * - Примечание: начиная с версии 1.2.2 второй аргумент $offset убран. Если
     * передавать его согласно старой сигнатуре, то он будет проигнорирован.
     *
     * @param int   $n       Количество ссылок, которые нужно вывести в текущем блоке
     * @param array $options Опции
     *
     * <code>
     * $options = array();
     * $options['block_no_css'] = (false|true);
     * // Переопределяет запрет на вывод css в коде страницы: false - выводить css
     * $options['block_orientation'] = (1|0);
     * // Переопределяет ориентацию блока: 1 - горизонтальная, 0 - вертикальная
     * $options['block_width'] = ('auto'|'[?]px'|'[?]%'|'[?]');
     * // Переопределяет ширину блока:
     * // 'auto'  - определяется шириной блока-предка с фиксированной шириной,
     * // если такового нет, то займет всю ширину
     * // '[?]px' - значение в пикселях
     * // '[?]%'  - значение в процентах от ширины блока-предка с фиксированной шириной
     * // '[?]'   - любое другое значение, которое поддерживается спецификацией CSS
     * </code>
     *
     * @see return_links()
     * @see return_counter()
     *
     * @return string
     */
    public function return_block_links($n = null, $options = null)
    {

        $numargs = func_num_args();
        $args    = func_get_args();

        //Проверяем аргументы для старой сигнатуры вызова
        if (2 == $numargs) {           // return_links($n, $options)
            if (!is_array($args[1])) { // return_links($n, $offset) - deprecated!
                $options = null;
            }
        } elseif (2 < $numargs) { // return_links($n, $offset, $options) - deprecated!

            if (!is_array($options)) {
                $options = $args[2];
            }
        }

        // Объединить параметры
        if (empty($options)) {
            $options = array();
        }

        $defaults                      = array();
        $defaults['block_no_css']      = false;
        $defaults['block_orientation'] = 1;
        $defaults['block_width']       = '';

        $ext_options = array();
        if (isset($this->_block_tpl_options) && is_array($this->_block_tpl_options)) {
            $ext_options = $this->_block_tpl_options;
        }

        $options = array_merge($defaults, $ext_options, $options);

        // Ссылки переданы не массивом (чек-код) => выводим как есть + инфо о блоке
        if (!is_array($this->_links_page)) {
            $html = $this->_return_array_links_html('', array('is_block_links' => true));

            return $this->_return_html($this->_links_page . $html);
        } // Не переданы шаблоны => нельзя вывести блоком - ничего не делать
        elseif (!isset($this->_block_tpl)) {
            return $this->_return_html('');
        }

        // Определим нужное число элементов в блоке

        $total_page_links = count($this->_links_page);

        $need_show_obligatory_block  = false;
        $need_show_conditional_block = false;
        $n_requested                 = 0;

        if (isset($this->_block_ins_itemobligatory)) {
            $need_show_obligatory_block = true;
        }

        if (is_numeric($n) && $n >= $total_page_links) {

            $n_requested = $n;

            if (isset($this->_block_ins_itemconditional)) {
                $need_show_conditional_block = true;
            }
        }

        if (!is_numeric($n) || $n > $total_page_links) {
            $n = $total_page_links;
        }

        // Выборка ссылок
        $links = array();
        for ($i = 1; $i <= $n; $i++) {
            $links[] = array_shift($this->_links_page);
        }

        $html = '';

        // Подсчет числа опциональных блоков
        $nof_conditional = 0;
        if (count($links) < $n_requested && true == $need_show_conditional_block) {
            $nof_conditional = $n_requested - count($links);
        }

        //Если нет ссылок и нет вставных блоков, то ничего не выводим
        if (empty($links) && $need_show_obligatory_block == false && $nof_conditional == 0) {

            $return_links_options = array(
                'is_block_links'      => true,
                'nof_links_requested' => $n_requested,
                'nof_links_displayed' => 0,
                'nof_obligatory'      => 0,
                'nof_conditional'     => 0
            );

            $html = $this->_return_array_links_html($html, $return_links_options);

            return $this->_return_html($html);
        }

        // Делаем вывод стилей, только один раз. Или не выводим их вообще, если так задано в параметрах
        $s_globals = new SAPE_globals();
        if (!$s_globals->block_css_shown() && false == $options['block_no_css']) {
            $html .= $this->_block_tpl['css'];
            $s_globals->block_css_shown(true);
        }

        // Вставной блок в начале всех блоков
        if (isset($this->_block_ins_beforeall) && !$s_globals->block_ins_beforeall_shown()) {
            $html .= $this->_block_ins_beforeall;
            $s_globals->block_ins_beforeall_shown(true);
        }
        unset($s_globals);

        // Вставной блок в начале блока
        if (isset($this->_block_ins_beforeblock)) {
            $html .= $this->_block_ins_beforeblock;
        }

        // Получаем шаблоны в зависимости от ориентации блока
        $block_tpl_parts = $this->_block_tpl[$options['block_orientation']];

        $block_tpl          = $block_tpl_parts['block'];
        $item_tpl           = $block_tpl_parts['item'];
        $item_container_tpl = $block_tpl_parts['item_container'];
        $item_tpl_full      = str_replace('{item}', $item_tpl, $item_container_tpl);
        $items              = '';

        $nof_items_total = count($links);
        foreach ($links as $link) {

            // Обычная красивая ссылка
            $is_found = preg_match('#<a href="(https?://([^"/]+)[^"]*)"[^>]*>[\s]*([^<]+)</a>#i', $link, $link_item);
            // Картиночкая красивая ссылка
            if (!$is_found) {
                preg_match('#<a href="(https?://([^"/]+)[^"]*)"[^>]*><img.*?alt="(.*?)".*?></a>#i', $link, $link_item);
            }

            if (function_exists('mb_strtoupper') && strlen($this->_sape_charset) > 0) {
                $header_rest         = mb_substr($link_item[3], 1, mb_strlen($link_item[3], $this->_sape_charset) - 1, $this->_sape_charset);
                $header_first_letter = mb_strtoupper(mb_substr($link_item[3], 0, 1, $this->_sape_charset), $this->_sape_charset);
                $link_item[3]        = $header_first_letter . $header_rest;
            } elseif (function_exists('ucfirst') && (strlen($this->_sape_charset) == 0 || strpos($this->_sape_charset, '1251') !== false)) {
                $link_item[3][0] = ucfirst($link_item[3][0]);
            }

            // Если есть раскодированный URL, то заменить его при выводе
            if (isset($this->_block_uri_idna) && isset($this->_block_uri_idna[$link_item[2]])) {
                $link_item[2] = $this->_block_uri_idna[$link_item[2]];
            }

            $item = $item_tpl_full;
            $item = str_replace('{header}', $link_item[3], $item);
            $item = str_replace('{text}', trim($link), $item);
            $item = str_replace('{url}', $link_item[2], $item);
            $item = str_replace('{link}', $link_item[1], $item);
            $items .= $item;
        }

        // Вставной обязатльный элемент в блоке
        if (true == $need_show_obligatory_block) {
            $items .= str_replace('{item}', $this->_block_ins_itemobligatory, $item_container_tpl);
            $nof_items_total += 1;
        }

        // Вставные опциональные элементы в блоке
        if ($need_show_conditional_block == true && $nof_conditional > 0) {
            for ($i = 0; $i < $nof_conditional; $i++) {
                $items .= str_replace('{item}', $this->_block_ins_itemconditional, $item_container_tpl);
            }
            $nof_items_total += $nof_conditional;
        }

        if ($items != '') {
            $html .= str_replace('{items}', $items, $block_tpl);

            // Проставляем ширину, чтобы везде одинковая была
            if ($nof_items_total > 0) {
                $html = str_replace('{td_width}', round(100 / $nof_items_total), $html);
            } else {
                $html = str_replace('{td_width}', 0, $html);
            }

            // Если задано, то переопределить ширину блока
            if (isset($options['block_width']) && !empty($options['block_width'])) {
                $html = str_replace('{block_style_custom}', 'style="width: ' . $options['block_width'] . '!important;"', $html);
            }
        }

        unset($block_tpl_parts, $block_tpl, $items, $item, $item_tpl, $item_container_tpl);

        // Вставной блок в конце блока
        if (isset($this->_block_ins_afterblock)) {
            $html .= $this->_block_ins_afterblock;
        }

        //Заполняем оставшиеся модификаторы значениями
        unset($options['block_no_css'], $options['block_orientation'], $options['block_width']);

        $tpl_modifiers = array_keys($options);
        foreach ($tpl_modifiers as $k => $m) {
            $tpl_modifiers[$k] = '{' . $m . '}';
        }
        unset($m, $k);

        $tpl_modifiers_values = array_values($options);

        $html = str_replace($tpl_modifiers, $tpl_modifiers_values, $html);
        unset($tpl_modifiers, $tpl_modifiers_values);

        //Очищаем незаполненные модификаторы
        $clear_modifiers_regexp = '#\{[a-z\d_\-]+\}#';
        $html                   = preg_replace($clear_modifiers_regexp, ' ', $html);

        $return_links_options = array(
            'is_block_links'      => true,
            'nof_links_requested' => $n_requested,
            'nof_links_displayed' => $n,
            'nof_obligatory'      => ($need_show_obligatory_block == true ? 1 : 0),
            'nof_conditional'     => $nof_conditional
        );

        $html = $this->_return_array_links_html($html, $return_links_options);

        return $this->_return_html($html);
    }

    /**
     * Вывод ссылок в обычном виде - текст с разделителем
     *
     * - Примечание: начиная с версии 1.2.2 второй аргумент $offset убран. Если
     * передавать его согласно старой сигнатуре, то он будет проигнорирован.
     *
     * @param int   $n       Количество ссылок, которые нужно вывести
     * @param array $options Опции
     *
     * <code>
     * $options = array();
     * $options['as_block'] = (false|true);
     * // Показывать ли ссылки в виде блока
     * </code>
     *
     * @see return_block_links()
     * @see return_counter()
     *
     * @return string
     */
    public function return_links($n = null, $options = null)
    {

        if ($this->_debug) {
            if (function_exists('debug_backtrace')) {
                $this->_return_links_calls[] = debug_backtrace();
            } else {
                $this->_return_links_calls = "(function_exists('debug_backtrace')==false";
            }
        }

        $numargs = func_num_args();
        $args    = func_get_args();

        //Проверяем аргументы для старой сигнатуры вызова
        if (2 == $numargs) {           // return_links($n, $options)
            if (!is_array($args[1])) { // return_links($n, $offset) - deprecated!
                $options = null;
            }
        } elseif (2 < $numargs) {        // return_links($n, $offset, $options) - deprecated!

            if (!is_array($options)) {
                $options = $args[2];
            }
        }

        //Опрелелить, как выводить ссылки
        $as_block = $this->_show_only_block;

        if (is_array($options) && isset($options['as_block']) && false == $as_block) {
            $as_block = $options['as_block'];
        }

        if (true == $as_block && isset($this->_block_tpl)) {
            return $this->return_block_links($n, $options);
        }

        //-------

        if (is_array($this->_links_page)) {

            $total_page_links = count($this->_links_page);

            if (!is_numeric($n) || $n > $total_page_links) {
                $n = $total_page_links;
            }

            $links = array();

            for ($i = 1; $i <= $n; $i++) {
                $links[] = array_shift($this->_links_page);
            }

            $html = $this->_convertCharset(join($this->_links_delimiter, $links));

            if ($this->_is_our_bot) {
                $html = '<sape_noindex>' . $html . '</sape_noindex>';
            }
        } else {
            $html = $this->_links_page;
            if ($this->_is_our_bot) {
                $html .= '<sape_noindex></sape_noindex>';
            }
        }

        $html = $this->_return_html($html);

        return $html;
    }

    public function return_teasers_block($block_id)
    {
        if ($this->_debug) {
            if (function_exists('debug_backtrace')) {
                $this->_return_links_calls[] = debug_backtrace();
            } else {
                $this->_return_links_calls = "(function_exists('debug_backtrace')==false";
            }
        }

        $html     = '';
        $template = @$this->_links['__sape_teasers_templates__'][$block_id];

        if (count($this->_teasers_page) && false == empty($template)) {

            if (count($this->_teasers_page) < $template['n']) {
                $teasers             = $this->_teasers_page;
                $to_add              = $template['n'] - count($this->_teasers_page);
                $this->_teasers_page = array();
            } else {
                $teasers             = array_slice($this->_teasers_page, 0, $template['n']);
                $to_add              = 0;
                $this->_teasers_page = array_slice($this->_teasers_page, $template['n']);
            }

            foreach ($teasers as $k => $v) {
                preg_match('#href="(https?://([^"/]+)[^"]*)"#i', $v, $url);
                $url         = empty($url[1]) ? '' : $url[1];
                $teasers[$k] = str_replace('{u}', $url, $template['bi'] . $v . $template['ai']);
            }

            if ($to_add) {
                $teasers = array_merge($teasers, array_fill($template['n'], $to_add, $template['e']));
            }

            $html = $this->_convertCharset(
                ($this->_teasers_css_showed ? '' : $this->_links['__sape_teasers_css__']) .
                str_replace('{i}', implode($template['d'], $teasers), $template['t'])
            );

            $this->_teasers_css_showed = true;
        } else {
            if ($this->_is_our_bot || $this->_force_show_code) {
                $html = $this->_links['__sape_new_teasers_block__'] . '<!-- ' . $block_id . ' -->';
            }
            if (!empty($template)) {
                $html .= str_replace('{id}', $block_id, $template['f']);
            } else {
                $this->_raise_error("Нет информации по блоку $block_id, обратитесь в службу поддержки");
            }
        }

        if ($this->_is_our_bot) {
            $html = '<sape_noindex>' . $html . '</sape_noindex>';
        }

        return $this->_add_debug_info($this->_return_obligatory_page_content() . $html);
    }

    public function show_image($file_name = null)
    {
        if ($this->_debug) {
            if (function_exists('debug_backtrace')) {
                $this->_return_links_calls[] = debug_backtrace();
            } else {
                $this->_return_links_calls = "(function_exists('debug_backtrace')==false";
            }
            echo $this->_add_debug_info('');
        }

        $file_name = $file_name ? $file_name : parse_url($this->_request_uri, PHP_URL_QUERY);

        if (!array_key_exists('__sape_teaser_images__', $this->_links) || !array_key_exists($file_name, $this->_links['__sape_teaser_images__'])) {
            $this->_raise_error("Нет файла изображения с именем '$file_name'");
            header("HTTP/1.0 404 Not Found");
        } else {
            $extension = pathinfo(strtolower($file_name), PATHINFO_EXTENSION);
            if ($extension == 'jpg') {
                $extension = 'jpeg';
            }

            header('Content-Type: image/' . $extension);
            header('Content-Length: ' . strlen($this->_links['__sape_teaser_images__'][$file_name]));
            header('Cache-control: public, max-age=604800'); //1 week

            echo $this->_links['__sape_teaser_images__'][$file_name];
        }
    }

    protected function _get_db_file()
    {
        if ($this->_multi_site) {
            return dirname(__FILE__) . '/' . $this->_host . '.links' . $this->_get_save_filename_prefix() . '.db';
        } else {
            return dirname(__FILE__) . '/links' . $this->_get_save_filename_prefix() . '.db';
        }
    }

    protected function _get_meta_file()
    {
        if ($this->_multi_site) {
            return dirname(__FILE__) . '/' . $this->_host . '.links.meta.db';
        } else {
            return dirname(__FILE__) . '/links.meta.db';
        }
    }

    protected function _get_dispenser_path()
    {
        return '/code.php?user=' . _SAPE_USER . '&host=' . $this->_host;
    }

    protected function _set_data($data)
    {
        if ($this->_ignore_case) {
            $this->_links = array_change_key_case($data);
        } else {
            $this->_links = $data;
        }
        if (isset($this->_links['__sape_delimiter__'])) {
            $this->_links_delimiter = $this->_links['__sape_delimiter__'];
        }
        // определяем кодировку кеша
        if (isset($this->_links['__sape_charset__'])) {
            $this->_sape_charset = $this->_links['__sape_charset__'];
        } else {
            $this->_sape_charset = '';
        }
        if (@array_key_exists($this->_request_uri, $this->_links) && is_array($this->_links[$this->_request_uri])) {
            $this->_links_page = $this->_links[$this->_request_uri];
        } else {
            if (isset($this->_links['__sape_new_url__']) && strlen($this->_links['__sape_new_url__'])) {
                if ($this->_is_our_bot || $this->_force_show_code) {
                    $this->_links_page = $this->_links['__sape_new_url__'];
                }
            }
        }

        if (@array_key_exists($this->_request_uri, $this->_links['__sape_teasers__']) && is_array($this->_links['__sape_teasers__'][$this->_request_uri])) {
            $this->_teasers_page = $this->_links['__sape_teasers__'][$this->_request_uri];
        }

        //Есть ли обязательный вывод
        if (isset($this->_links['__sape_page_obligatory_output__'])) {
            if ($this->_teasers_rtb_proxy !== null) {
                $this->_page_obligatory_output = $this->_teasers_rtb_proxy->return_script();
            } else {
                $this->_page_obligatory_output = $this->_links['__sape_page_obligatory_output__'];
            }
        }

        // Есть ли флаг блочных ссылок
        if (isset($this->_links['__sape_show_only_block__'])) {
            $this->_show_only_block = $this->_links['__sape_show_only_block__'];
        } else {
            $this->_show_only_block = false;
        }

        // Есть ли шаблон для красивых ссылок
        if (isset($this->_links['__sape_block_tpl__']) && !empty($this->_links['__sape_block_tpl__'])
            && is_array($this->_links['__sape_block_tpl__'])
        ) {
            $this->_block_tpl = $this->_links['__sape_block_tpl__'];
        }

        // Есть ли параметры для красивых ссылок
        if (isset($this->_links['__sape_block_tpl_options__']) && !empty($this->_links['__sape_block_tpl_options__'])
            && is_array($this->_links['__sape_block_tpl_options__'])
        ) {
            $this->_block_tpl_options = $this->_links['__sape_block_tpl_options__'];
        }

        // IDNA-домены
        if (isset($this->_links['__sape_block_uri_idna__']) && !empty($this->_links['__sape_block_uri_idna__'])
            && is_array($this->_links['__sape_block_uri_idna__'])
        ) {
            $this->_block_uri_idna = $this->_links['__sape_block_uri_idna__'];
        }

        // Блоки
        $check_blocks = array(
            'beforeall',
            'beforeblock',
            'afterblock',
            'itemobligatory',
            'itemconditional',
            'afterall'
        );

        foreach ($check_blocks as $block_name) {

            $var_name  = '__sape_block_ins_' . $block_name . '__';
            $prop_name = '_block_ins_' . $block_name;

            if (isset($this->_links[$var_name]) && strlen($this->_links[$var_name]) > 0) {
                $this->$prop_name = $this->_links[$var_name];
            }
        }
    }

    protected function _uncode_data($data)
    {
        if ($this->_format == 'php-require') {
            $data1 = str_replace('<?php return ', '', $data);
            eval('$data = ' . $data1 . ';');
            return $data;
        }

        return @unserialize($data);
    }

    protected function _code_data($data)
    {
        if ($this->_format == 'php-require') {
            return var_export($data, true);
        }

        return @serialize($data);
    }

    protected function _save_data($data, $filename = '')
    {
        if ($this->_split_data_file) {
            $directory = dirname(__FILE__) . '/';
            $hashArray = array();
            $data = $this->_uncode_data($data);
            foreach ($data as $url => $item) {
                if (preg_match('/\_\_.+\_\_/mu', $url)) {
                    $currentFile = 'links.meta.db';
                } else {
                    $currentFile = 'links.' . crc32($url) % 100 . '.db';
                }
                if ($this->_multi_site) {
                    $currentFile = $this->_host . '.' . $currentFile;
                }
                $hashArray[$currentFile][$url] = $item;
            }
            foreach ($hashArray as $file => $array) {
                $this->_write($directory . $file, $this->_code_data($array));
            }
            if (!isset($hashArray[basename($filename)])) {
                parent::_save_data('', $filename);
            }
        } else {
            parent::_save_data($data, $filename);
        }
    }
}

/**
 * Класс для работы с контекстными ссылками
 */
class SAPE_context extends SAPE_base
{

    protected $_words       = array();
    protected $_words_page  = array();
    protected $_user_agent  = 'SAPE_Context PHP';
    protected $_filter_tags = array('a', 'textarea', 'select', 'script', 'style', 'label', 'noscript', 'noindex', 'button');

    protected $_debug_actions = array();

    public function __construct($options = null)
    {
        parent::__construct($options);
        $this->_load_data();
    }

    /**
     * Начать сбор дебаг-информации
     */
    protected function _debug_action_start()
    {
        if (!$this->_debug) {
            return;
        }

        $this->_debug_actions   = array();
        $this->_debug_actions[] = $this->_get_full_user_agent_string();
    }

    /**
     * Записать строку дебаг-информацию
     *
     * @param        $data
     * @param string $key
     */
    protected function _debug_action_append($data, $key = '')
    {
        if (!$this->_debug) {
            return;
        }

        if (!empty($key)) {
            $this->_debug_actions[] = array($key => $data);
        } else {
            $this->_debug_actions[] = $data;
        }
    }

    /**
     * Вывод дебаг-информации
     *
     * @return string
     */
    protected function _debug_action_output()
    {

        if (!$this->_debug || empty($this->_debug_actions)) {
            return '';
        }

        $debug_info = $this->_debug_output($this->_debug_actions);

        $this->_debug_actions = array();

        return $debug_info;
    }

    /**
     * Замена слов в куске текста и обрамляет его тегами sape_index
     */
    public function replace_in_text_segment($text)
    {

        $this->_debug_action_start();
        $this->_debug_action_append('START: replace_in_text_segment()');
        $this->_debug_action_append($text, 'argument for replace_in_text_segment');

        if (count($this->_words_page) > 0) {

            $source_sentences = array();

            //Создаем массив исходных текстов для замены
            foreach ($this->_words_page as $n => $sentence) {
                //Заменяем все сущности на символы
                $special_chars = array(
                    '&amp;'  => '&',
                    '&quot;' => '"',
                    '&#039;' => '\'',
                    '&lt;'   => '<',
                    '&gt;'   => '>'
                );
                $sentence      = strip_tags($sentence);
                $sentence      = strip_tags($sentence);
                $sentence      = str_replace(array_keys($special_chars), array_values($special_chars), $sentence);

                //Преобразуем все спец символы в сущности
                $htsc_charset = empty($this->_charset) ? 'windows-1251' : $this->_charset;
                $quote_style  = ENT_COMPAT;
                if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
                    $quote_style = ENT_COMPAT | ENT_HTML401;
                }

                $sentence = htmlspecialchars($sentence, $quote_style, $htsc_charset);

                //Квотируем
                $sentence      = preg_quote($sentence, '/');
                $replace_array = array();
                if (preg_match_all('/(&[#a-zA-Z0-9]{2,6};)/isU', $sentence, $out)) {
                    for ($i = 0; $i < count($out[1]); $i++) {
                        $unspec                 = $special_chars[$out[1][$i]];
                        $real                   = $out[1][$i];
                        $replace_array[$unspec] = $real;
                    }
                }
                //Заменяем сущности на ИЛИ (сущность|символ)
                foreach ($replace_array as $unspec => $real) {
                    $sentence = str_replace($real, '((' . $real . ')|(' . $unspec . '))', $sentence);
                }
                //Заменяем пробелы на переносы или сущности пробелов
                $source_sentences[$n] = str_replace(' ', '((\s)|(&nbsp;))+', $sentence);
            }

            $this->_debug_action_append($source_sentences, 'sentences for replace');

            //если это первый кусок, то не будем добавлять <
            $first_part = true;
            //пустая переменная для записи

            if (count($source_sentences) > 0) {

                $content   = '';
                $open_tags = array(); //Открытые забаненые тэги
                $close_tag = ''; //Название текущего закрывающего тэга

                //Разбиваем по символу начала тега
                $part = strtok(' ' . $text, '<');

                while ($part !== false) {
                    //Определяем название тэга
                    if (preg_match('/(?si)^(\/?[a-z0-9]+)/', $part, $matches)) {
                        //Определяем название тега
                        $tag_name = strtolower($matches[1]);
                        //Определяем закрывающий ли тэг
                        if (substr($tag_name, 0, 1) == '/') {
                            $close_tag = substr($tag_name, 1);
                            $this->_debug_action_append($close_tag, 'close tag');
                        } else {
                            $close_tag = '';
                            $this->_debug_action_append($tag_name, 'open tag');
                        }
                        $cnt_tags = count($open_tags);
                        //Если закрывающий тег совпадает с тегом в стеке открытых запрещенных тегов
                        if (($cnt_tags > 0) && ($open_tags[$cnt_tags - 1] == $close_tag)) {
                            array_pop($open_tags);

                            $this->_debug_action_append($tag_name, 'deleted from open_tags');

                            if ($cnt_tags - 1 == 0) {
                                $this->_debug_action_append('start replacement');
                            }
                        }

                        //Если нет открытых плохих тегов, то обрабатываем
                        if (count($open_tags) == 0) {
                            //если не запрещенный тэг, то начинаем обработку
                            if (!in_array($tag_name, $this->_filter_tags)) {
                                $split_parts = explode('>', $part, 2);
                                //Перестраховываемся
                                if (count($split_parts) == 2) {
                                    //Начинаем перебор фраз для замены
                                    foreach ($source_sentences as $n => $sentence) {
                                        if (preg_match('/' . $sentence . '/', $split_parts[1]) == 1) {
                                            $split_parts[1] = preg_replace('/' . $sentence . '/', str_replace('$', '\$', $this->_words_page[$n]), $split_parts[1], 1);

                                            $this->_debug_action_append($sentence . ' --- ' . $this->_words_page[$n], 'replaced');

                                            //Если заменили, то удаляем строчку из списка замены
                                            unset($source_sentences[$n]);
                                            unset($this->_words_page[$n]);
                                        }
                                    }
                                    $part = $split_parts[0] . '>' . $split_parts[1];
                                    unset($split_parts);
                                }
                            } else {
                                //Если у нас запрещеный тэг, то помещаем его в стек открытых
                                $open_tags[] = $tag_name;

                                $this->_debug_action_append($tag_name, 'added to open_tags, stop replacement');
                            }
                        }
                    } elseif (count($open_tags) == 0) {
                        //Если нет названия тега, то считаем, что перед нами текст
                        foreach ($source_sentences as $n => $sentence) {
                            if (preg_match('/' . $sentence . '/', $part) == 1) {
                                $part = preg_replace('/' . $sentence . '/', str_replace('$', '\$', $this->_words_page[$n]), $part, 1);

                                $this->_debug_action_append($sentence . ' --- ' . $this->_words_page[$n], 'replaced');

                                //Если заменили, то удаляем строчку из списка замены,
                                //чтобы было можно делать множественный вызов
                                unset($source_sentences[$n]);
                                unset($this->_words_page[$n]);
                            }
                        }
                    }

                    //Если это первая часть, то не выводим <
                    if ($first_part) {
                        $content .= $part;
                        $first_part = false;
                    } else {
                        $content .= '<' . $part;
                    }
                    //Получаем следующу часть
                    unset($part);
                    $part = strtok('<');
                }
                $text = ltrim($content);
                unset($content);
            }
        } else {
            $this->_debug_action_append('No word\'s for page');
        }

        if ($this->_is_our_bot || $this->_force_show_code || $this->_debug) {
            $text = '<sape_index>' . $text . '</sape_index>';
            if (isset($this->_words['__sape_new_url__']) && strlen($this->_words['__sape_new_url__'])) {
                $text .= $this->_words['__sape_new_url__'];
            }
        }

        if (count($this->_words_page) > 0) {
            $this->_debug_action_append($this->_words_page, 'Not replaced');
        }

        $this->_debug_action_append('END: replace_in_text_segment()');

        $text .= $this->_debug_action_output();

        return $text;
    }

    /**
     * Замена слов
     */
    public function replace_in_page($buffer)
    {

        $this->_debug_action_start();
        $this->_debug_action_append('START: replace_in_page()');

        $s_globals = new SAPE_globals();

        if (!$s_globals->page_obligatory_output_shown()
            && isset($this->_page_obligatory_output)
            && !empty($this->_page_obligatory_output)
        ) {

            $split_content = preg_split('/(?smi)(<\/?body[^>]*>)/', $buffer, -1, PREG_SPLIT_DELIM_CAPTURE);
            if (count($split_content) == 5) {
                $buffer = $split_content[0] . $split_content[1] . $split_content[2]
                    . (false == $this->_show_counter_separately ? $this->_return_obligatory_page_content() : '')
                    . $split_content[3] . $split_content[4];
                unset($split_content);

                $s_globals->page_obligatory_output_shown(true);
            }
        }

        if (count($this->_words_page) > 0) {
            //разбиваем строку по sape_index
            //Проверяем есть ли теги sape_index
            $split_content = preg_split('/(?smi)(<\/?sape_index>)/', $buffer, -1);
            $cnt_parts     = count($split_content);
            if ($cnt_parts > 1) {
                //Если есть хоть одна пара sape_index, то начинаем работу
                if ($cnt_parts >= 3) {
                    for ($i = 1; $i < $cnt_parts; $i = $i + 2) {
                        $split_content[$i] = $this->replace_in_text_segment($split_content[$i]);
                    }
                }
                $buffer = implode('', $split_content);

                $this->_debug_action_append($cnt_parts, 'Split by Sape_index cnt_parts=');
            } else {
                //Если не нашли sape_index, то пробуем разбить по BODY
                $split_content = preg_split('/(?smi)(<\/?body[^>]*>)/', $buffer, -1, PREG_SPLIT_DELIM_CAPTURE);
                //Если нашли содержимое между body
                if (count($split_content) == 5) {
                    $split_content[0] = $split_content[0] . $split_content[1];
                    $split_content[1] = $this->replace_in_text_segment($split_content[2]);
                    $split_content[2] = $split_content[3] . $split_content[4];
                    unset($split_content[3]);
                    unset($split_content[4]);
                    $buffer = $split_content[0] . $split_content[1] . $split_content[2];

                    $this->_debug_action_append('Split by BODY');
                } else {
                    //Если не нашли sape_index и не смогли разбить по body
                    $this->_debug_action_append('Cannot split by BODY');
                }
            }
        } else {
            if (!$this->_is_our_bot && !$this->_force_show_code && !$this->_debug) {
                $buffer = preg_replace('/(?smi)(<\/?sape_index>)/', '', $buffer);
            } else {
                if (isset($this->_words['__sape_new_url__']) && strlen($this->_words['__sape_new_url__'])) {
                    $buffer .= $this->_words['__sape_new_url__'];
                }
            }

            $this->_debug_action_append('No word\'s for page');
        }

        $this->_debug_action_append('STOP: replace_in_page()');
        $buffer .= $this->_debug_action_output();

        return $buffer;
    }

    protected function _get_db_file()
    {
        if ($this->_multi_site) {
            return dirname(__FILE__) . '/' . $this->_host . '.words' . $this->_get_save_filename_prefix() . '.db';
        } else {
            return dirname(__FILE__) . '/words' . $this->_get_save_filename_prefix() . '.db';
        }
    }

    protected function _get_meta_file()
    {
        if ($this->_multi_site) {
            return dirname(__FILE__) . '/' . $this->_host . '.words.meta.db';
        } else {
            return dirname(__FILE__) . '/words.meta.db';
        }
    }

    protected function _get_dispenser_path()
    {
        return '/code_context.php?user=' . _SAPE_USER . '&host=' . $this->_host;
    }

    protected function _set_data($data)
    {
        $this->_words = $data;
        if (@array_key_exists($this->_request_uri, $this->_words) && is_array($this->_words[$this->_request_uri])) {
            $this->_words_page = $this->_words[$this->_request_uri];
        }

        //Есть ли обязательный вывод
        if (isset($this->_words['__sape_page_obligatory_output__'])) {
            $this->_page_obligatory_output = $this->_words['__sape_page_obligatory_output__'];
        }
    }

    protected function _uncode_data($data)
    {
        if ($this->_format == 'php-require') {
            $data1 = str_replace('<?php return ', '', $data);
            eval('$data = ' . $data1 . ';');
            return $data;
        }

        return @unserialize($data);
    }

    protected function _code_data($data)
    {
        if ($this->_format == 'php-require') {
            return var_export($data, true);
        }

        return @serialize($data);
    }

    protected function _save_data($data, $filename = '')
    {
        if ($this->_split_data_file) {
            $directory = dirname(__FILE__) . '/';
            $hashArray = array();
            $data = $this->_uncode_data($data);
            foreach ($data as $url => $item) {
                if (preg_match('/\_\_.+\_\_/mu', $url)) {
                    $currentFile = 'words.meta.db';
                } else {
                    $currentFile = 'words.' . crc32($url) % 100 . '.db';
                }
                if ($this->_multi_site) {
                    $currentFile = $this->_host . '.' . $currentFile;
                }
                $hashArray[$currentFile][$url] = $item;
            }
            foreach ($hashArray as $file => $array) {
                $this->_write($directory . $file, $this->_code_data($array));
            }
            if (!isset($hashArray[basename($filename)])) {
                parent::_save_data('', $filename);
            }
        } else {
            parent::_save_data($data, $filename);
        }
    }
}

/**
 * Класс для работы со статьями articles.sape.ru показывает анонсы и статьи
 */
class SAPE_articles extends SAPE_base
{
    const INTEGRATION_TYPE_WORDPRESS = 2;

    protected $_request_mode;

    protected $_server_list = array('dispenser.articles.sape.ru');

    protected $_data = array();

    protected $_article_id;

    protected $_save_file_name;

    protected $_announcements_delimiter = '';

    protected $_images_path;

    protected $_template_error = false;

    protected $_noindex_code = '<!--sape_noindex-->';

    protected $_headers_enabled = false;

    protected $_mask_code;

    protected $_real_host;

    protected $_user_agent = 'SAPE_Articles_Client PHP';

    public function __construct($options = null)
    {
        parent::__construct($options);
        if (is_array($options) && isset($options['headers_enabled'])) {
            $this->_headers_enabled = $options['headers_enabled'];
        }
        // Кодировка
        if (isset($options['charset']) && strlen($options['charset'])) {
            $this->_charset = $options['charset'];
        } else {
            $this->_charset = '';
        }
        $this->_get_index();
        if (!empty($this->_data['index']['announcements_delimiter'])) {
            $this->_announcements_delimiter = $this->_data['index']['announcements_delimiter'];
        }
        if (!empty($this->_data['index']['charset'])
            and !(isset($options['charset']) && strlen($options['charset']))
        ) {
            $this->_charset = $this->_data['index']['charset'];
        }
        if (is_array($options)) {
            if (isset($options['host'])) {
                $host = $options['host'];
            }
        } elseif (strlen($options)) {
            $host    = $options;
            $options = array();
        }
        if (isset($host) && strlen($host)) {
            $this->_real_host = $host;
        } else {
            $this->_real_host = $_SERVER['HTTP_HOST'];
        }
        if (!isset($this->_data['index']['announcements'][$this->_request_uri])) {
            $this->_correct_uri();
        }
        $this->_split_data_file = false;
    }

    protected function _correct_uri()
    {
        if (substr($this->_request_uri, -1) == '/') {
            $new_uri = substr($this->_request_uri, 0, -1);
        } else {
            $new_uri = $this->_request_uri . '/';
        }
        if (isset($this->_data['index']['announcements'][$new_uri])) {
            $this->_request_uri = $new_uri;
        }
    }

    /**
     * Возвращает анонсы для вывода
     *
     * @param int $n      Сколько анонсов вывести, либо не задано - вывести все
     * @param int $offset C какого анонса начинаем вывод(нумерация с 0), либо не задано - с нулевого
     *
     * @return string
     */
    public function return_announcements($n = null, $offset = 0)
    {
        $output = '';
        if ($this->_force_show_code || $this->_is_our_bot) {
            if (isset($this->_data['index']['checkCode'])) {
                $output .= $this->_data['index']['checkCode'];
            }
        }

        if (false == $this->_show_counter_separately) {
            $output .= $this->_return_obligatory_page_content();
        }

        if (isset($this->_data['index']['announcements'][$this->_request_uri])) {

            $total_page_links = count($this->_data['index']['announcements'][$this->_request_uri]);

            if (!is_numeric($n) || $n > $total_page_links) {
                $n = $total_page_links;
            }

            $links = array();

            for ($i = 1; $i <= $n; $i++) {
                if ($offset > 0 && $i <= $offset) {
                    array_shift($this->_data['index']['announcements'][$this->_request_uri]);
                } else {
                    $links[] = array_shift($this->_data['index']['announcements'][$this->_request_uri]);
                }
            }

            $html = join($this->_announcements_delimiter, $links);

            if ($this->_is_our_bot) {
                $html = '<sape_noindex>' . $html . '</sape_noindex>';
            }

            $output .= $html;
        }

        return $output;
    }

    /**
     * Основной метод при работе в режиме интеграции с CMS Wordpress
     *
     * @param $newArticles
     * @param $updateArticles
     * @param $deletedArticles
     * @param $upload_base_dir
     */
    public function wp_process(&$newArticles, &$updateArticles, &$deletedArticles, $upload_base_dir)
    {
        // Инициализация файла работы с WordPress
        $this->_wp_init();

        if ((int)$this->_data['index']['integration_type'] == self::INTEGRATION_TYPE_WORDPRESS) {
            // Список статей на диспенсере
            $dispenserArticles = array();
            if (isset($this->_data['index']['articles'])) {
                foreach ($this->_data['index']['articles'] as $article) {
                    $dispenserArticles[(int)$article['id']] = array(
                        'id'           => (int)$article['id'],
                        'date_updated' => (int)$article['date_updated']
                    );
                }
            }

            // Список статей из WordPress-а
            $wpArticles = $this->_data['wp'];

            $dispenserArticleIds = array_keys($dispenserArticles);
            $wpArticleIds        = array_keys($wpArticles);
            $unionArticlesIds    = array_merge($dispenserArticleIds, $wpArticleIds);

            foreach ($unionArticlesIds as $articleId) {
                // Новые статьи
                if (in_array($articleId, $dispenserArticleIds) && !in_array($articleId, $wpArticleIds)) {
                    $this->_load_wp_article($dispenserArticles[$articleId]);

                    $newArticles[$articleId] = array(
                        'id'          => (int)$articleId,
                        'title'       => $this->_data['article']['title'],
                        'keywords'    => $this->_data['article']['keywords'],
                        'description' => $this->_data['article']['description'],
                        'body'        => $this->_data['article']['body'],
                    );
                }

                // Существующие статьи
                if (in_array($articleId, $dispenserArticleIds) && in_array($articleId, $wpArticleIds)) {
                    $this->_load_wp_article($dispenserArticles[$articleId]);

                    if (
                        $this->_data['article']['title'] != $this->_data['wp'][$articleId]['wp_post_title']
                        ||
                        $this->_data['article']['body'] != $this->_data['wp'][$articleId]['wp_post_content']
                    ) {
                        $updateArticles[$articleId] = array(
                            'id'          => (int)$articleId,
                            'wp_post_id'  => $this->_data['wp'][$articleId]['wp_post_id'],
                            'title'       => $this->_data['article']['title'],
                            'keywords'    => $this->_data['article']['keywords'],
                            'description' => $this->_data['article']['description'],
                            'body'        => $this->_data['article']['body'],
                        );
                    }
                }

                // Снятые статьи
                if (!in_array($articleId, $dispenserArticleIds) && in_array($articleId, $wpArticleIds)) {
                    $deletedArticles[$articleId] = array(
                        'id'         => (int)$articleId,
                        'wp_post_id' => (int)$wpArticles[$articleId]['wp_post_id']
                    );
                }
            }

            // Работа с изображениями
            if (isset($this->_data['index']['images'])) {
                foreach ($this->_data['index']['images'] as $image_uri => $image_meta) {
                    $this->_load_wp_image($image_uri, $image_meta['article_id'], $upload_base_dir);
                }
            }
        }
    }

    /**
     * Массив идентификаторов постов движка Wordpress,
     * которые были созданы в режиме интеграции
     *
     * @return array
     */
    public function wp_get_post_ids()
    {
        $wpPostIds = array();

        // Инициализация файла работы с WordPress
        $this->_wp_init();

        // Список статей из WordPress-а
        $wpArticles = $this->_data['wp'];

        foreach ($wpArticles as $wpArticle) {
            $wpPostIds[] = (int)$wpArticle['wp_post_id'];
        }

        return $wpPostIds;
    }

    /**
     * Сохранение информации о постах движка Wordpress,
     * которые были созданы в режиме интеграции
     *
     * @param        $posts
     * @param string $mode
     */
    public function wp_save_local_db($posts, $mode = 'add')
    {
        if (isset($posts) && is_array($posts)) {
            $this->_save_file_name = 'articles.wp.db';
            $this->_db_file        = dirname(__FILE__) . '/' . $this->_host . '.' . $this->_save_file_name;

            foreach ($posts as $articleId => $post) {
                if (in_array($mode, array('add', 'update'))) {
                    $this->_data['wp'][$articleId] = $post;
                }
                if ($mode == 'delete') {
                    unset($this->_data['wp'][$articleId]);
                }
            }

            $this->_save_data(serialize($this->_data['wp']), $this->_db_file);
        }
    }

    /**
     * Передача диспенсеру УРЛов размещенных статей,
     * созданных в режиме интеграции
     *
     * @param $posts
     * @param $upload_base_url
     */
    public function wp_push_posts($posts, $upload_base_url)
    {
        $this->_set_request_mode('article');

        if (isset($posts) && is_array($posts)) {
            foreach ($posts as $articleId => $post) {
                $this->_article_id = (int)$articleId;
                $path              = $this->_get_dispenser_path();
                $path_postfix      = '&set_article_url=' . urlencode($post['wp_post_url']);
                $path_postfix      .= '&set_article_image_url=' . urlencode($upload_base_url . '/' . (int)$articleId . '/');

                foreach ($this->_server_list as $server) {
                    if ($data = $this->_fetch_remote_file($server, $path . $path_postfix)) {
                        if (substr($data, 0, 12) != 'FATAL ERROR:') {
                            break;
                        }
                        $this->_raise_error($data);
                    }
                }
            }

            // Обновляем индекс
            $this->_save_file_name = 'articles.db';
            unlink($this->_get_db_file());
            $this->_get_index();
        }
    }

    /**
     * Инициализация режима интеграции с CMS Wordpress
     */
    protected function _wp_init()
    {
        $this->_set_request_mode('wp');
        $this->_save_file_name = 'articles.wp.db';
        $this->_load_wp_data();
    }

    protected function _get_index()
    {
        $this->_set_request_mode('index');
        $this->_save_file_name = 'articles.db';
        $this->_load_data();
    }

    /**
     * Возвращает полный HTML код страницы статьи
     * @return string
     */
    public function process_request()
    {
        if (!empty($this->_data['index']) and isset($this->_data['index']['articles'][$this->_request_uri])) {
            return $this->_return_article();
        } elseif (!empty($this->_data['index']) and isset($this->_data['index']['images'][$this->_request_uri])) {
            return $this->_return_image();
        } else {
            if ($this->_is_our_bot) {
                return $this->_return_html($this->_data['index']['checkCode'] . $this->_noindex_code);
            } else {
                return $this->_return_not_found();
            }
        }
    }

    protected function _return_article()
    {
        $this->_set_request_mode('article');
        //Загружаем статью
        $article_meta          = $this->_data['index']['articles'][$this->_request_uri];
        $this->_save_file_name = $article_meta['id'] . '.article.db';
        $this->_article_id     = $article_meta['id'];
        $this->_load_data();
        if (false == $this->_show_counter_separately) {
            $this->_data[$this->_request_mode]['body'] = $this->_return_obligatory_page_content() . $this->_data[$this->_request_mode]['body'];
        }

        //Обновим если устарела
        if (!isset($this->_data['article']['date_updated']) OR $this->_data['article']['date_updated'] < $article_meta['date_updated']) {
            unlink($this->_get_db_file());
            $this->_load_data();
        }

        //Получим шаблон
        $template = $this->_get_template($this->_data['index']['templates'][$article_meta['template_id']]['url'], $article_meta['template_id']);

        //Выведем статью
        $article_html = $this->_fetch_article($template);

        if ($this->_is_our_bot) {
            $article_html .= $this->_noindex_code;
        }

        return $this->_return_html($article_html);
    }

    /**
     * Загрузка статьи в режиме интеграции CMS Wordpress
     *
     * @param $article_meta
     */
    protected function _load_wp_article($article_meta)
    {
        $this->_set_request_mode('article');

        //Загружаем статью
        $this->_save_file_name = (int)$article_meta['id'] . '.article.db';
        $this->_article_id     = (int)$article_meta['id'];
        $this->_load_data();
        if (false == $this->_show_counter_separately) {
            $this->_data[$this->_request_mode]['body'] = $this->_return_obligatory_page_content() . $this->_data[$this->_request_mode]['body'];
        }

        //Обновим если устарела
        if (!isset($this->_data['article']['date_updated']) OR $this->_data['article']['date_updated'] < $article_meta['date_updated']) {
            unlink($this->_get_db_file());
            $this->_load_data();
        }
    }

    protected function _prepare_path_to_images()
    {
        $this->_images_path = dirname(__FILE__) . '/images/';
        if (!is_dir($this->_images_path)) {
            // Пытаемся создать папку.
            if (@mkdir($this->_images_path)) {
                @chmod($this->_images_path, 0777);    // Права доступа
            } else {
                return $this->_raise_error('Нет папки ' . $this->_images_path . '. Создать не удалось. Выставите права 777 на папку.');
            }
        }
        if ($this->_multi_site) {
            $this->_images_path .= $this->_host . '.';
        }

        return true;
    }

    /**
     * Создание папки для хранения изображений статьи
     * в режиме интеграции с CMS Wordpress
     *
     * @param $article_id
     * @param $upload_base_dir
     *
     * @return bool
     */
    protected function _prepare_wp_path_to_images($article_id, $upload_base_dir)
    {
        $this->_images_path = $upload_base_dir . '/' . (int)$article_id . '/';

        if (!is_dir($this->_images_path)) {
            // Пытаемся создать папку.
            if (@mkdir($this->_images_path)) {
                @chmod($this->_images_path, 0777);    // Права доступа
            } else {
                return $this->_raise_error('Нет папки ' . $this->_images_path . '. Создать не удалось. Выставите права 777 на папку.');
            }
        }

        return true;
    }

    protected function _return_image()
    {
        $this->_set_request_mode('image');
        $this->_prepare_path_to_images();

        //Проверим загружена ли картинка
        $image_meta = $this->_data['index']['images'][$this->_request_uri];
        $image_path = $this->_images_path . $image_meta['id'] . '.' . $image_meta['ext'];

        if (!is_file($image_path) or filemtime($image_path) != $image_meta['date_updated']) {
            // Чтобы не повесить площадку клиента и чтобы не было одновременных запросов
            @touch($image_path, $image_meta['date_updated']);

            $path = $image_meta['dispenser_path'];

            foreach ($this->_server_list as $server) {
                if ($data = $this->_fetch_remote_file($server, $path)) {
                    if (substr($data, 0, 12) == 'FATAL ERROR:') {
                        $this->_raise_error($data);
                    } else {
                        // [псевдо]проверка целостности:
                        if (strlen($data) > 0) {
                            $this->_write($image_path, $data);
                            break;
                        }
                    }
                }
            }
            @touch($image_path, $image_meta['date_updated']);
        }

        unset($data);
        if (!is_file($image_path)) {
            return $this->_return_not_found();
        }
        $image_file_meta = @getimagesize($image_path);
        $content_type    = isset($image_file_meta['mime']) ? $image_file_meta['mime'] : 'image';
        if ($this->_headers_enabled) {
            header('Content-Type: ' . $content_type);
        }

        return $this->_read($image_path);
    }

    /**
     * Загрузка изображения статьи в режиме
     * интеграции с CMS Wordpress
     *
     * @param $image_uri
     * @param $article_id
     * @param $upload_base_dir
     */
    protected function _load_wp_image($image_uri, $article_id, $upload_base_dir)
    {
        $this->_request_uri = $image_uri;
        $this->_set_request_mode('image');

        $this->_prepare_wp_path_to_images($article_id, $upload_base_dir);

        //Проверим загружена ли картинка
        $image_meta = $this->_data['index']['images'][$this->_request_uri];
        $image_path = $this->_images_path . $image_meta['filename'];

        if (!is_file($image_path) || filemtime($image_path) != $image_meta['date_updated']) {
            // Чтобы не повесить площадку клиента и чтобы не было одновременных запросов
            @touch($image_path, $image_meta['date_updated']);

            $path = $image_meta['dispenser_path'];
            foreach ($this->_server_list as $server) {
                if ($data = $this->_fetch_remote_file($server, $path)) {
                    if (substr($data, 0, 12) == 'FATAL ERROR:') {
                        $this->_raise_error($data);
                    } else {
                        // [псевдо]проверка целостности:
                        if (strlen($data) > 0) {
                            $this->_write($image_path, $data);
                            break;
                        }
                    }
                }
            }
            @touch($image_path, $image_meta['date_updated']);
        }
    }

    protected function _fetch_article($template)
    {
        if (strlen($this->_charset)) {
            $template = str_replace('{meta_charset}', $this->_charset, $template);
        }
        foreach ($this->_data['index']['template_fields'] as $field) {
            if (isset($this->_data['article'][$field])) {
                $template = str_replace('{' . $field . '}', $this->_data['article'][$field], $template);
            } else {
                $template = str_replace('{' . $field . '}', '', $template);
            }
        }

        return ($template);
    }

    protected function _get_template($template_url, $templateId)
    {
        //Загрузим индекс если есть
        $this->_save_file_name = 'tpl.articles.db';
        $index_file            = $this->_get_db_file();

        if (file_exists($index_file)) {
            $this->_data['templates'] = unserialize($this->_read($index_file));
        }


        //Если шаблон не найден или устарел в индексе, обновим его
        if (!isset($this->_data['templates'][$template_url])
            or (time() - $this->_data['templates'][$template_url]['date_updated']) > $this->_data['index']['templates'][$templateId]['lifetime']
        ) {
            $this->_refresh_template($template_url, $index_file);
        }
        //Если шаблон не обнаружен - ошибка
        if (!isset($this->_data['templates'][$template_url])) {
            if ($this->_template_error) {
                return $this->_raise_error($this->_template_error);
            }

            return $this->_raise_error('Не найден шаблон для статьи');
        }

        return $this->_data['templates'][$template_url]['body'];
    }

    protected function _refresh_template($template_url, $index_file)
    {
        $parseUrl = parse_url($template_url);

        $download_url = '';
        if ($parseUrl['path']) {
            $download_url .= $parseUrl['path'];
        }
        if (isset($parseUrl['query'])) {
            $download_url .= '?' . $parseUrl['query'];
        }

        $template_body = $this->_fetch_remote_file($this->_real_host, $download_url, true);

        //проверим его на корректность
        if (!$this->_is_valid_template($template_body)) {
            return false;
        }

        $template_body = $this->_cut_template_links($template_body);

        //Запишем его вместе с другими в кэш
        $this->_data['templates'][$template_url] = array('body' => $template_body, 'date_updated' => time());
        //И сохраним кэш
        $this->_write($index_file, serialize($this->_data['templates']));

        return true;
    }

    public function _fill_mask($data)
    {
        global $unnecessary;
        $len                              = strlen($data[0]);
        $mask                             = str_repeat($this->_mask_code, $len);
        $unnecessary[$this->_mask_code][] = array(
            'mask' => $mask,
            'code' => $data[0],
            'len'  => $len
        );

        return $mask;
    }

    protected function _cut_unnecessary(&$contents, $code, $mask)
    {
        global $unnecessary;
        $this->_mask_code                = $code;
        $_unnecessary[$this->_mask_code] = array();
        $contents                        = preg_replace_callback($mask, array($this, '_fill_mask'), $contents);
    }

    protected function _restore_unnecessary(&$contents, $code)
    {
        global $unnecessary;
        $offset = 0;
        if (!empty($unnecessary[$code])) {
            foreach ($unnecessary[$code] as $meta) {
                $offset   = strpos($contents, $meta['mask'], $offset);
                $contents = substr($contents, 0, $offset)
                    . $meta['code'] . substr($contents, $offset + $meta['len']);
            }
        }
    }

    protected function _cut_template_links($template_body)
    {
        if (function_exists('mb_internal_encoding') && strlen($this->_charset) > 0) {
            mb_internal_encoding($this->_charset);
        }
        $link_pattern    = '~(\<a [^\>]*?href[^\>]*?\=["\']{0,1}http[^\>]*?\>.*?\</a[^\>]*?\>|\<a [^\>]*?href[^\>]*?\=["\']{0,1}http[^\>]*?\>|\<area [^\>]*?href[^\>]*?\=["\']{0,1}http[^\>]*?\>)~si';
        $link_subpattern = '~\<a |\<area ~si';
        $rel_pattern     = '~[\s]{1}rel\=["\']{1}[^ "\'\>]*?["\']{1}| rel\=[^ "\'\>]*?[\s]{1}~si';
        $href_pattern    = '~[\s]{1}href\=["\']{0,1}(http[^ "\'\>]*)?["\']{0,1} {0,1}~si';

        $allowed_domains   = $this->_data['index']['ext_links_allowed'];
        $allowed_domains[] = $this->_host;
        $allowed_domains[] = 'www.' . $this->_host;
        $this->_cut_unnecessary($template_body, 'C', '|<!--(.*?)-->|smi');
        $this->_cut_unnecessary($template_body, 'S', '|<script[^>]*>.*?</script>|si');
        $this->_cut_unnecessary($template_body, 'N', '|<noindex[^>]*>.*?</noindex>|si');

        $slices = preg_split($link_pattern, $template_body, -1, PREG_SPLIT_DELIM_CAPTURE);
        //Обрамляем все видимые ссылки в noindex
        if (is_array($slices)) {
            foreach ($slices as $id => $link) {
                if ($id % 2 == 0) {
                    continue;
                }
                if (preg_match($href_pattern, $link, $urls)) {
                    $parsed_url = @parse_url($urls[1]);
                    $host       = isset($parsed_url['host']) ? $parsed_url['host'] : false;
                    if (!in_array($host, $allowed_domains) || !$host) {
                        //Обрамляем в тэги noindex
                        $slices[$id] = '<noindex>' . $slices[$id] . '</noindex>';
                    }
                }
            }
            $template_body = implode('', $slices);
        }
        //Вновь отображаем содержимое внутри noindex
        $this->_restore_unnecessary($template_body, 'N');

        //Прописываем всем ссылкам nofollow
        $slices = preg_split($link_pattern, $template_body, -1, PREG_SPLIT_DELIM_CAPTURE);
        if (is_array($slices)) {
            foreach ($slices as $id => $link) {
                if ($id % 2 == 0) {
                    continue;
                }
                if (preg_match($href_pattern, $link, $urls)) {
                    $parsed_url = @parse_url($urls[1]);
                    $host       = isset($parsed_url['host']) ? $parsed_url['host'] : false;
                    if (!in_array($host, $allowed_domains) || !$host) {
                        //вырезаем REL
                        $slices[$id] = preg_replace($rel_pattern, '', $link);
                        //Добавляем rel=nofollow
                        $slices[$id] = preg_replace($link_subpattern, '$0rel="nofollow" ', $slices[$id]);
                    }
                }
            }
            $template_body = implode('', $slices);
        }

        $this->_restore_unnecessary($template_body, 'S');
        $this->_restore_unnecessary($template_body, 'C');

        return $template_body;
    }

    protected function _is_valid_template($template_body)
    {
        foreach ($this->_data['index']['template_required_fields'] as $field) {
            if (strpos($template_body, '{' . $field . '}') === false) {
                $this->_template_error = 'В шаблоне не хватает поля ' . $field . '.';

                return false;
            }
        }

        return true;
    }

    protected function _return_html($html)
    {
        if ($this->_headers_enabled) {
            header('HTTP/1.x 200 OK');
            if (!empty($this->_charset)) {
                header('Content-Type: text/html; charset=' . $this->_charset);
            }
        }

        return $html;
    }

    protected function _return_not_found()
    {
        header('HTTP/1.x 404 Not Found');
    }

    protected function _get_dispenser_path()
    {
        switch ($this->_request_mode) {
            case 'index':
                return '/?user=' . _SAPE_USER . '&host=' .
                    $this->_host . '&rtype=' . $this->_request_mode;
                break;
            case 'article':
                return '/?user=' . _SAPE_USER . '&host=' .
                    $this->_host . '&rtype=' . $this->_request_mode . '&artid=' . $this->_article_id;
                break;
            case 'image':
                return $this->image_url;
                break;
        }
    }

    protected function _set_request_mode($mode)
    {
        $this->_request_mode = $mode;
    }

    protected function _get_db_file()
    {
        if ($this->_multi_site) {
            return dirname(__FILE__) . '/' . $this->_host . '.' . $this->_save_file_name;
        } else {
            return dirname(__FILE__) . '/' . $this->_save_file_name;
        }
    }

    protected function _set_data($data)
    {
        $this->_data[$this->_request_mode] = $data;
        //Есть ли обязательный вывод
        if (isset($data['__sape_page_obligatory_output__'])) {
            $this->_page_obligatory_output = $data['__sape_page_obligatory_output__'];
        }
    }

    /**
     * Загрузка данных WordPress
     */
    protected function _load_wp_data()
    {
        $this->_db_file = dirname(__FILE__) . '/' . $this->_host . '.' . $this->_save_file_name;

        if (!file_exists($this->_db_file)) {
            // Пытаемся создать файл.
            if (@touch($this->_db_file)) {
                @chmod($this->_db_file, 0666); // Права доступа
            } else {
                return $this->_raise_error('Нет файла ' . $this->_db_file . '. Создать не удалось. Выставите права 777 на папку.');
            }
            $this->_write($this->_db_file, serialize(array()));
        }

        if (!is_writable($this->_db_file)) {
            return $this->_raise_error('Нет доступа на запись к файлу: ' . $this->_db_file . '! Выставите права 777 на папку.');
        }

        @clearstatcache();

        $data = $this->_read($this->_db_file);
        $data = $this->_uncode_data($data);

        $this->_set_data($data);

        return true;
    }

    protected function _get_meta_file()
    {
        return $this->_get_db_file();
    }
}

/**
 * Класс для работы клиентским кодом rtb.sape.ru
 */
class SAPE_rtb extends SAPE_base
{

    protected $_site_id = null;

    protected $_ucode_id = null;

    protected $_ucode_url = null;

    protected $_ucode_filename = null;

    protected $_ucode_places = array();

    protected $_base_dir = null;

    protected $_base_url = '/';

    protected $_proxy_url = null;

    protected $_data = null;

    protected $_filename = null;

    protected $_server_list = array('rtb.sape.ru');

    protected $_format = false;

    protected $_split_data_file = false;

    protected $_return_script_shown = false;

    /**
     * SAPE_rtb constructor.
     *
     * @param array $options
     */
    public function __construct($options = null)
    {
        if (isset($options['host'])) {
            $this->_host = $options['host'];
        } else {
            $this->_host = $_SERVER['HTTP_HOST'];
            $this->_host = preg_replace('/^http(?:s)?:\/\//', '', $this->_host);
            $this->_host = preg_replace('/^www\./', '', $this->_host);
        }

        if (isset($options['ucode_id'])) {
            $this->_ucode_id = $options['ucode_id'];
            if (isset($options['ucode_filename'])) {
                $this->_filename = preg_replace('~\.js$~', '', trim($options['ucode_filename'])) . '.js';
            } else {
                $this->_filename = $this->_ucode_id . '.js';
            }
            if (isset($options['filename'])) {
                $this->_ucode_filename = preg_replace('~\.js$~', '', trim($options['filename'])) . '.js';
            }
            if (isset($options['places'])) {
                $this->_ucode_places = $options['places'];
            }
        } elseif (isset($options['site_id'])) {
            $this->_site_id = $options['site_id'];
            if (isset($options['filename']) && $options['filename']) {
                $this->_filename = preg_replace('~\.js$~', '', trim($options['filename'])) . '.js';
            } else {
                $this->_filename = $this->_site_id . '.js';
            }
        }

        if ($this->_filename !== null) {
            if (isset($options['base_dir'])) {
                $this->_base_dir = preg_replace('~/$~', '', trim($options['base_dir'])) . '/';
            } else {
                $this->_base_dir = dirname(dirname(__FILE__)) . '/';
            }

            if (isset($options['base_url'])) {
                $this->_base_url = preg_replace('~/$~', '', trim($options['base_url'])) . '/';
            }

            if (isset($options['proxy_url'])) {
                $this->_proxy_url = strpos($options['proxy_url'], '?') === false ? ($options['proxy_url'] . '?') : (preg_replace('~&^~', '', '&' . $options['proxy_url'] . '&'));
            } else {
                $this->_proxy_url = '/proxy.php?';
            }

            $this->_load_data();
        } else {
            $this->_load_proxed_url();
        }
    }

    /**
     * Получить имя файла с даными
     *
     * @return string
     */
    protected function _get_db_file()
    {
        if ($this->_ucode_id) {
            return dirname(__FILE__) . '/rtb.ucode.' . $this->_ucode_id . '.' . $this->_host . '.db';
        }

        return dirname(__FILE__) . '/rtb.site.' . $this->_site_id . '.' . $this->_host . '.db';
    }

    /**
     * Получить URI к хосту диспенсера
     *
     * @return string
     */
    protected function _get_dispenser_path()
    {
        if ($this->_ucode_id) {
            return '/dispenser/user/' . _SAPE_USER . '/' . $this->_ucode_id;
        }

        return '/dispenser/site/' . _SAPE_USER . '/' . $this->_site_id;
    }

    /**
     * @return bool
     */
    protected function _load_proxed_url()
    {
        $db_file = dirname(__FILE__) . '/rtb.proxy.db';
        if (!is_file($db_file)) {
            if (@touch($db_file)) {
                @chmod($db_file, 0666); // Права доступа
            } else {
                return $this->_raise_error('Нет файла ' . $db_file . '. Создать не удалось. Выставите права 777 на папку.');
            }
        }
        if (!is_writable($db_file)) {
            return $this->_raise_error('Нет доступа на запись к файлу: ' . $db_file . '! Выставите права 777 на папку.');
        }

        @clearstatcache();

        $data = $this->_read($db_file);
        if ($data !== '') {
            $this->_data['__proxy__'] = $this->_uncode_data($data);
        }

        return true;
    }

    /**
     * Сохранение данных в файл.
     *
     * @param string $data
     * @param string $filename
     */
    protected function _save_data($data, $filename = '')
    {
        $hash = $this->_uncode_data($data);
        if (isset($hash['__code__']) && !empty($hash['__code__'])) {
            $this->_save_data_js($hash);
        }

        parent::_save_data($data, $filename);
    }

    /**
     * Сохранение данных в js файл.
     *
     * @param array $data
     */
    protected function _save_data_js($data)
    {
        $code = null;
        if ($this->_ucode_id) {
            if (!empty($data['__sites__'])) {
                $key = crc32($this->_host) . crc32(strrev($this->_host));
                if (isset($data['__sites__'][$key])) {
                    $script = new SAPE_rtb(array('site_id' => $data['__sites__'][$key], 'base_dir' => $this->_base_dir, 'filename' => $this->_ucode_filename));
                    $script = $script->return_script_url();
                    if (!empty($script)) {
                        $code = '(function(w,n,m){w[n]=' . json_encode($this->_proxy_url) . ';w[m]=' . json_encode($script) . ';})(window,"srtb_proxy","srtb_proxy_site");' . $data['__code__'];
                    }
                }
            }
        }

        if ($code === null) {
            $code = '(function(w,n){w[n]=' . json_encode($this->_proxy_url) . ';})(window,"srtb_proxy");' . $data['__code__'];
        }

        $this->_write($this->_base_dir . $this->_filename, $code);
        $this->_write(dirname(__FILE__) . '/rtb.proxy.db', $this->_code_data($data['__proxy__']));
    }

    /**
     * Сохранить данные, полученные из файла, в объекте
     *
     * @param array $data
     */
    protected function _set_data($data)
    {
        $this->_data = $data;
    }

    /**
     * @return string
     */
    protected function return_script_url()
    {
        return '//' . $this->_host . $this->_base_url . $this->_filename . '?t=' . filemtime($this->_db_file);
    }

    /**
     * @return string
     */
    public function return_script()
    {
        if ($this->_return_script_shown === false && !empty($this->_data) && !empty($this->_data['__code__'])) {
            $this->_return_script_shown = true;

            $js = $this->_base_dir . $this->_filename;
            if (!(file_exists($js) && is_file($js))) {
                $this->_save_data_js($this->_data);
            }

            if ($this->_ucode_places) {
                $params = '';
                foreach ($this->_ucode_places as $place) {
                    $params .= 'w[n].push(' . json_encode($place) . ');';
                }

                return '<script type="text/javascript">(function(w,d,n){w[n]=w[n]||[];' . $params . '})(window,document,"srtb_places");</script><script type="text/javascript" src="' . $this->return_script_url() . '" async="async"></script>';
            }

            return '<script type="text/javascript" src="' . $this->return_script_url() . '" async="async"></script>';
        }

        return '';
    }

    /**
     * @param integer $block_id
     *
     * @return string
     */
    public function return_block($block_id)
    {
        if ($this->_site_id && isset($this->_data['__ads__'][$block_id])) {
            return '<!-- SAPE RTB DIV ' . $this->_data['__ads__'][$block_id]['w'] . 'x' . $this->_data['__ads__'][$block_id]['h'] . ' --><div id="SRTB_' . (int)$block_id . '"></div><!-- SAPE RTB END -->';
        }

        return '';
    }

    /**
     * @param array $options
     *
     * @return string
     */
    public function return_ucode($options)
    {
        if ($this->_ucode_id) {
            $params = '';
            foreach ($options as $key => $val) {
                $params .= ' data-ad-' . $key . '="' .  htmlspecialchars($val, ENT_QUOTES) . '"';
            }

            return '<div class="srtb-tag-' . $this->_ucode_id . '" style="display:inline-block;"' . $params . '></div>';
        }

        return '';
    }

    /**
     * @return bool
     */
    public function process_request()
    {
        if (isset($_GET['q']) && !empty($this->_data['__proxy__'])) {
            $url = @base64_decode($_GET['q']);
            if ($url !== false) {
                $test   = false;
                $prefix = preg_replace('~^(?:https?:)//~', '', $url);
                foreach ($this->_data['__proxy__'] as $u) {
                    if (strpos($u, $prefix) !== 0) {
                        $test = true;
                        break;
                    }
                }
                if ($test === false) {
                    $url = false;
                }
            }

            if ($url !== false) {
                if (strpos($url, '//') === 0) {
                    $url = 'http:' . $url;
                }
                if ($ch = @curl_init()) {
                    $headers = array();
                    if (function_exists('getallheaders')) {
                        $headers = getallheaders();
                    } else {
                        foreach ($_SERVER as $name => $value) {
                            if (substr($name, 0, 5) == 'HTTP_') {
                                $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
                            }
                        }
                    }

                    @curl_setopt($ch, CURLOPT_URL, $url);
                    @curl_setopt($ch, CURLOPT_HEADER, true);
                    @curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                    @curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->_socket_timeout);
                    @curl_setopt($ch, CURLOPT_USERAGENT, isset($headers['User-Agent']) ? $headers['User-Agent'] : (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ''));
                    @curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

                    $data       = @curl_exec($ch);
                    $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
                    $headerText = substr($data, 0, $headerSize);
                    $data       = substr($data, $headerSize);

                    @curl_close($ch);

                    foreach (explode("
", $headerText) as $i => $line) {
                        if ($line) {
                            header($line);
                        }
                    }

                    echo $data;
                }

                return true;
            }
        }

        header('HTTP/1.x 404 Not Found');

        return false;
    }
}
   

Anon7 - 2022
AnonSec Team